为什么我从零开始学AI Agent?一个4个月求职计划

104 阅读29分钟

序幕

最近在春招,也投了一些简历,发现不尽人意。但是我发现AI发展才刚起步,我不想再错过这个风口了,尽管我不是人工智能科班出生,但是我仍然想通过学习ai为自己以后做一些铺垫。所以从今天开始,正式学习ai,并且让deepseek给我设置里一条从0出发的路径。 第一周计划如下:

image.png

加油少年,为自己某一条出路,跟上时代的步伐。


Day1:

Task_1: 安装软件

下载好了vscode 和 Python 3.1.13 并且成功运行了hello.py文件,输出hello agent! 哈哈有点hello world异曲同工之妙。

屏幕截图 2026-03-21 142856.png

Task_2: 体验coze

下载并体验coze,发布了自己的第一个天气大模型,明白了原来大模型可以调用插件,并且通过设置调教他,例如回答后必须加上出行的建议。我想让他是什么样子他就是什么样子。这是第一次体会到如何调用并调教agent,期待后续自己手搓agent。

屏幕截图 2026-03-21 145215.png

Day2:

Task_1: 学习字符串处理和数字处理

  • 字符串拼接: +
  • 字符串大小写处理: .upper() .lower()
  • 数字转字符串拼接: str()
  • 字符串转数字运算: int() float()
  • 理解:键盘直接输入的是字符串,是一种符号;想要做运算需要类型转换 str转换后的是字符串 变量存储的是数字 num_1 = 5 这是数字,想要拼接需要类型转换
    output_1 =  input('please enter a number_1')
    output_2 =  input('please enter a number_2')
    print(float(output_1) + float(output_2))
    
    

屏幕截图 2026-03-21 174934.png

Task_2 学习日期处理

  • datetime库 timedelta库
  • 现在时间:datetime.now()
  • 时间差值:timedelta(days=1)
  • 将输入的字符串日期转换为日期类型:datetime.strptime(birthday, '%d/%m/%Y')
from datetime import datetime,timedelta

current_date = datetime.now()  #调用现在时间函数
print('Today is ' + str(current_date))

one_time = timedelta(days=1)
two_time = timedelta(days=2)  #时间差函数

yesterday = current_date - one_time
qian_tian = current_date - two_time
tomorrow = current_date + one_time

print('yesterday is ' + str(yesterday))
print('qian_tian is ' + str(qian_tian))
print('tomorrow is ' + str(tomorrow))

print('day is ' + str(current_date.day)) 
print('mon is ' + str(current_date.month))
print('yesr is ' + str(current_date.year))  

birthday = input('When is you r birthday(dd/mm/yyyy)? : ') #因为是输入字符串,所以需要转换成日期类型
birthday_date = datetime.strptime(birthday, '%d/%m/%Y')  #存储年月日 转换日期类型函数
y_birthday_date = birthday_date - one_time
print('birthday is :' + str(birthday_date))
print('y_birthday is :' + str(y_birthday_date))

Task_3 学习条件判断

学习if elif :后面要有冒号

in and or :跟在if后面

bool :判断后执行更简洁

# province = input('enter province :')
# if province == 'jx':
#     tax = .07
# elif province == 'hn':
#     tax = .09
# print(tax)


# province = input('enter province :')
# if province == 'jx' \
#     or province == 'hn':
#     tax = .07
# elif province == 'cq':
#     tax = .09
# print(tax)


# province = input('enter province :')
# if province in('jx','hn','cq'):
#     tax = .07
# elif province == 'xz':
#     tax = .09
# else:
#     tax = 0
# print(tax)

gpa = input('what is your number ? :')
if float(gpa) <= 2 or float(gpa) >= 5:
    honur_roll = False
elif float(gpa) <= 4.9 and float(gpa) >= 2.1:
    honur_roll = True
if honur_roll:
    print('good')

Task_4 学习列表和字典

  • 列表[ ] 有序

       处理列表:
           列表插入 people.append({'cc' : 'ee'})     
           列表赋值 people = [person_1]
           列表索引 people[0]   第一个
           列表索引 people[0:2]0开始 找两个 
           列表长度 .len()
    
  • 字典{ } 自定义命名

       处理字典:
           字典插入:person_1['last'] = 'jf' 
           字典赋值:person_2 = {'first' :'l','last' :'jy' }   
    
    # jay = array('d')    # ???  不清楚
    # jay.append(99)
    # print(jay)


    person_1 = {}                  #   字典{}  
    person_1['last'] = 'jf'        #   往字典里插值 person[] = ''
    person_1['first'] = 'xia'

    person_2 = {'first' :'l','last' :'jy' } #字典 person = {'first' : 'jx', 'second' : 'yr'} 键值对

    print(person_1)
    print(person_2)                #  打印字典


    people = [person_1]            #  列表[] 往列表里 插字典
    print(people)                  #  打印列表

    people.append({'aa' : 'bb'})   #  往列表里 拼接字典
    people.append({'cc' : 'ee'})   #  往列表里 拼接字典
    print(people)                  #  打印列表

    print(people[0])               #  索引 打印列表1
    presenters = people[0:2]       #  从0开始 数两个
    print(presenters)

    print(len(presenters))         #  数 数量 2
    print(len(people))

Task_5 学习循环

while要递增,控制数量 for比较无脑

# names = ['xjf','lyr','hz']
# index = 0
# while index < len(names):
#     print(names[index])
#     index = index + 1


for names in ('x','j','f'):
    print(names)

Task_6 学习函数

参数用名字=对应,就不用在乎顺序了

# def get_initial(name):
#     initial = name[0:10]
#     return initial
# first_name = input('please enter your first name :')
# last_name = input('please enter your last name :')

# print(get_initial(first_name))
# print(get_initial(last_name))

# print(get_initial(first_name)+get_initial(last_name))


from datetime import datetime 
index = 0

def print_time(index):
    print('NO.',index)
    print(datetime.now())
    print()

first_name = 'x'

for index in range(0,10):
    print_time(index)

Task_7 学习模块和包

模块是分装好的一群函数方法,包是模块的集合

from x import display //只要某一个方法 也可变成 * 所有方法 display()

当然也可以写出 import x 但是用方法的时候就得加前缀 x.display()

Task_8 学习安装使用包

第一步:安装vsc整个虚拟环境 在终端输入python -m venv venv 第一个venv是虚拟空间 第二个venv是目录名称

第二步:想要在此项目本地执行虚拟环境 在终端输入.\venv\Scritps\activate.ps1

第三步:在侧边栏创建requirements.txt文件,文件内 写 外部包名字 colorama

第四步:将包激活 pip install -r requirements.txt 这样包就安装好了,可以尽情的调用包中的函数了 from x import y

Task_9 学习如何调取API

  • 先注册好azure,创建资源,记住密钥
  • 利用函数读取图片二进制数据
  • 将发送请求需要的参数赋好值:api地址、认证、查询参数、图片二进制数据
  • 发送HTTO POST请求
  • 检查发送状态
  • 解析收到的JSON
  • 打印解析结果
主要逻辑
try:
    # 文件读取(微软推荐的with语句)
    with open(image_path, "rb") as image_file:
        image_data = image_file.read()  # ⚠️ 第二次读取,覆盖之前的数据
    
    # 发送HTTP POST请求
    response = requests.post(
        vision_service_address,  # API地址
        headers=headers,         # 认证和内容类型头
        params=parameters,       # 查询参数
        data=image_data          # 图片二进制数据
    )
    
    # 检查HTTP状态码
    response.raise_for_status()  # 非200状态码会抛出异常
    
    # 解析JSON响应
    results = response.json()
import json
from pathlib import Path


SUBSCRIPTION_KEY = ''
vision_service_address = 'https://japanwest.api.cognitive.microsoft.com/vision/v3.2/analyze'
image_path = Path('image') / 'a.png'
image_data = open(image_path,"rb").read()
parameters = {
    'visualFeatures': 'Description,Color',  # 注意:Color首字母大写
    'language': 'en'
}
# ✅ 请求头
headers = {
    'Content-Type': 'application/octet-stream',
    'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
try:
    # ✅ 使用with语句打开文件(微软推荐)
    with open(image_path, "rb") as image_file:
        image_data = image_file.read()
    
    print(f"正在分析图片: {image_path}")
    print(f"图片大小: {len(image_data)} 字节")
    
    # ✅ 发送请求
    response = requests.post(
        vision_service_address,
        headers=headers,
        params=parameters,
        data=image_data
    )
    
    # ✅ 检查响应
    response.raise_for_status()
    
    # ✅ 解析结果
    results = response.json()
    print("✅ 分析成功!")
    print(json.dumps(results, indent=2, ensure_ascii=False))
    
except FileNotFoundError:
    print(f"❌ 错误:找不到图片文件 {image_path}")
    print("请确认:")
    print("1. 图片文件是否存在?")
    print("2. 路径是否正确?")
    print(f"当前目录: {Path.cwd()}")
    
except requests.exceptions.HTTPError as e:
    print(f"❌ API调用错误: {e}")
    print(f"状态码: {response.status_code}")
    print(f"响应内容: {response.text}")
    
except Exception as e:
    print(f"❌ 其他错误: {e}")

Day_3

Task_1 学习json处理

  • 先定义字典 dict = { }
  • 分别向字典中插入键值对、列表、字典 dict['键名'] = '值'
  • 通过json.jumps(dict)函数 将字典(数据结构)转换为json格式(字符串)
  • 注意:字典一定有键:值;如果只有键,那就变成集合set了,集合不能赋值和通过索引查值,只能通过add()remove()discard()增删元素
  • results['键名'] 可以索引查询需要的json数据,或者用for循环遍历查询多项

person_dict = {'first':'x','last':'jf'}
print('在字典中插入键值对:')
person_dict['city'] = 'nc'   #在字典中 插入 键值对

person_json = json.dumps(person_dict)  #json是一种字典,把字典转为json格式
print(person_json)
print(person_json[0])

language_list = ['c','java','c++']
print('在字典中插入列表:')
person_dict['language'] = language_list  #在字典中 插入 列表

print(person_dict)

staff_dict = {'name1':'zs','name2':'ls','name3':'ww'}
# staff_dict = {}
print('在字典中插入字典:')
staff_dict['program'] = person_dict  #在字典中 插入 字典
person_json = json.dumps(staff_dict)
print(staff_dict)

Task_2 学习环境变量的配置

因为当我们连接数据库以及服务的时候,隐私数据是不能公开的,所以我们需要使用环境变量保护数据。

  • 首先创建.env文件和.gitignore,并在.env文件中存储PASSWORD,然后把.env文件放入.gitignore中保护起来。
  • 现在我们的隐私数据都存起来了,那么如何调用呢?
  • 先需要我们在requirem.txt文件中写入python-dotenv包,然后在终端pip install -r requirements.txt 激活包。
  • 使用这个包是为了使用其中的几个函数对.env文件的数据进行读取,这样就形成了完整的逻辑链条。

image.png

image.png

image.png

import os
from dotenv import load_dotenv
# 加载.env文件
load_dotenv()

# 获取环境变量
password = os.getenv('PASSWORD')
print(password)

Task_3 学习装饰器

使用装饰器目的是,在不改变原有原函数的基础上,为原函数增添一些新的逻辑功能,变成一个新的函数,但是函数名称保持不变。
装饰器的核心是闭包。什么是闭包?在了解闭包之前,我们还需先了解一个函数def wrapper()

def wrapper():
    print("即将执行函数...")
    func()
    print("函数执行完毕。")

这样直观的展示可以看到,wrapper() 把func()函数包装了一下。
当wrapper函数被内置在装饰器下时,且装饰器传入一个由函数担任的参数时,闭包就此被创建。

def simple_decorator(func): # 传入一个函数作为参数
    def wrapper():
        print("即将执行函数...")
        func()
        print("函数执行完毕。")
    return wrapper # 返回wrapper

所以,我们到此时就可以知道了
装饰器是simple_decorator()
装饰器的参数是func函数
闭包是调用了func函数的装饰器simple_decorator(func)。
所以给闭包下个定义:指的是在函数被定义的时候,不仅能够记住函数自身,还能够记住外部环境,即func()函数。尽管func函数已经执行完毕会被销毁,但是由于闭包的特性,func会被wrapper一直记住,不会销毁
上述只是定义了一个装饰器,还没有真正的应用落地,怎么操作呢?它本身的逻辑是

#先定义装饰器:
def simple_decorator(func):
    def wrapper():
        print("即将执行函数...")
        func()
        print("函数执行完毕。")
    return wrapper
    
#再定义green函数    
def green()
    print(''我是green函数)
    
#装饰器正式应用。
#此时一个新函数closure正式成立,他由wrapper函数加green函数组成,成为一个闭包,也是新函数closure
closure = simple_decorator(green)
#调用新函数
closure()

#但是这样太拖沓了,既然是为了让green函数变得更多功能,那么我们把新函数继续定义成原来名称green函数。
green = simple_decorator(green)
#调用新函数
green()

#这样看还是有一些复杂,所以再优化一下,正式成为装饰器。在定义green 函数时,就使用装饰器。这样就省略了green = simple_decorator(green) 这一步
@simple_decorator
def green()
    print('我是green函数')
#直接在定义green函数时,就把green函数传进装饰器了,并返回一个函数wrapper,然后再执行green = wrapper赋值,这样变成了新green函数。

所以最终版本如下

def logger(func):
    def wrapper():
        print('111111')
        func()
        print('333333')
    return wrapper

@logger
def sample():
    print('--222222')

sample()

结果显示:调用sample函数后,打印如下,sample变成了一个新函数

111111
222222
333333

最核心的步骤是:经过语法糖,返回一个wrapper,并被赋值给了sample函数,
即sample = logger(sample)
@logger替代的就是 调用 返回 并赋值这三步
注意是替代,而不是省略,因为实质仍是sample = logger(sample) 这样执行

Task_4 最简单地模拟agent逻辑

也学到了f-string这个格式,允许在字符串中直接嵌入表达式 f'文本 {表达式} 文本' 研究了很久,终于同步到了git仓库,已经是凌晨1.48了。
仓库地址:MHDGhua/ai-agent-learning
如何推送更新仓库:git add . && git commit -m "本次修改内容" && git push

#模拟agent
user_message = input('你想查询什么?')
tool = ['天气查询','时间查询','播放音乐']

if '天气' in user_message:
    print('正在查询当前天气······')
    selected_tool = tool[0] 
    result = {'北京':'23摄氏度'}
    print(result)

elif'时间' in user_message:
    selected_tool = tool[1] 
    result = {'上海':'2026-3-23-23.17'}
    print(result)

elif'音乐' in user_message:
    selected_tool = tool[2] 
    result = {'网易云':'一直很安静'}
    print(result)

else:
    print('抱歉')
    print('done')
    

Day_3

Task_1 正式学习agent,学习模拟ReAct Agent逻辑

ReAct模式:

  • 按照用户输入
  • ->thought
  • ->action
  • ->observation
  • ····循环thought······
  • ->thought
  • ->final answer 此时,执行完毕

image.png

image.png

Task_2 跑通真实天气React_Agent

今天整了一天,总算把一个真实的天气agent跑通了,不容易。
初学总是会有很多问题,这个看不懂,那个不明白,光去调一个api都不明白怎么弄,只能一点一点摸索。会用api了,又发现代理又有问题,最后实在解决不了,就换回了国内的api。可是换回来了,一些关键的数据又找不到,host找了半天,最终还是调用成功了。
也明白了api的参数格式其实文档里面规定好了的,他就是这样的,没有为什么,约定俗成。当然其中还有很多方法都是没见过的,很多格式也没见过,都要一条一条的去问去了解。多亏了deepseek耐心的替我解答,放在几年前,今天一天的工程没有一个月是搞不定的,还得从csdn里面到处搜索查询别人踩过的坑。初学总是这样的,一点一点积累。
今天的成果更多的是明白了agent的完整逻辑构造。封装好工具和llm调用方法,然后写好核心agent,把环境变量配置好,与每个文件连接好。整体逻辑链条是这样,其中的每个方法中具体逻辑是如何,还需要慢慢练习体会。先让整个框架跑通来,就已经完成一半了,然后再慢慢知道细节。第一个agent也到这里成了,后面慢慢巩固和扩展知识。

Day_4

Task_1 学习self-attention

1. 为什么需要self-attention?

*   是因为一个一个向量vector传进去,机器读不懂我们的含义。我们需要考虑上下文。
*   传统使用的RNN 是循环连接 串行的逻辑,有几个弊端,对于离得远的序列顾及不上,而且运行效率低,只能按时间顺序运行
*   现在的:先self-attention处理整个sequence,再用Fully connected network处理某一个位置的资讯

self-attention是并行逻辑,无需考虑距离因素

image.png

2. self-attention是如何运作的呢?

选一个向量,将这个向量与每一个向量的k做关联性计算,得到一排相关性α

屏幕截图 2026-03-25 141629.png

3. 相关性又是如何计算得到的呢?

通过点积得出-----相关性α

屏幕截图 2026-03-25 141607.png

4. 举例计算 a1 与 a1自身 a2 a3 a4相关性 a q k

q1 = wq · a1 ------q1是基准

k1 = wk · a1\ ------k1是基准 k2 = wk · a2
k3 = wk · a3
k4 = wk · a4

α11 = q1 · k1 ------ a11 自身的相关性
α12 = q1 · k2
α13 = q1 · k3
α14 = q1 · k4

image.png

5. 怎么知道向量与其他向量的相关性强弱? -注意力权重--概率权重 和 概率加权--与v相乘---聚合value

分别以a1 a2 a3 a4为基准,经过点积、scale、soft-max,得到α1' α2' α3'
α1' α2' α3'分别与 vi 加权后,得到b1 b2 b3 b4
相关性越大,两个bi间最接近

image.png

image.png

image.png

6. 完整计算过程

并行:通过矩阵同步计算

image.png

image.png

image.png

整个流程图解:仅wq wk wv是变量,其他的都是规定的流程 image.png

Task_3 学习 Multi-head-self-attention 多模态自注意

为什么需要进阶版Multi-head-self-attention呢?是因为事情是复杂的,相关性是多维度的,需要多个q来表示同一个向量与其他向量之间不同维度的相关性,所以需要多个head。
而实现逻辑是一致的,且是多个q并行处理

屏幕截图 2026-03-25 161254.png

image.png

Task_4 学习Positional Encoding

引入位置编码,是因为每一个向量a其实对于机器来说是没有任何区别的,但是人看是有顺序的,所以给他一个位置编码,让他偏向原本的位置

image.png

Task_5 学习Encoder

Transformer 是纯Attention 的Seq2Seq

1.Seq2Seq 逻辑:

  • output sequence
  • Decoder ->
  • Encoder ->
  • input sequence ->

2.Encoder:

  • 由多个Block叠成

3.Block逻辑:

  • vector
  • attention
  • 层归一化 [pre-ln]
  • 残差连接
  • 1个前馈网络子层 (FFN:两个FC层)
  • 层归一化
  • 残差连接
  • 1个多头自注意力子层(Multi-Head Self-Attention),1个多头自注意力可以用12个q(可设定)
  • vector

image.png image.png

Task_6 学习 Decoder

1. AT Decoder 和 NAT Decoder

  • AT Decoder是串行输出,准但是慢。
  • NAT Decoder是并行一次性输出,效率比较高,但是不准 屏幕截图 2026-03-25 232242.png

2. 总的流程图

image.png

3. Masked Self-Attention

  • 输出时,每个vector都采用串行。每个vector做masked attention运算, 其中bi是一个一个出来的,关联性之后,最后会得到一个结果新向量。
  • 每个vector同时进行mask attention,会同时产生一个q,参与cross attention
  • 注意: 每一个vector是采用mask attention 串行处理得到q,但是所有的vector同时进行mask attention ,同时得到q
  • 为什么是串行? 因为这是预测,不是像输入一样所有都是确定的,能够全部丢进去并行处理,输出时只能用之前出现过的,

屏幕截图 2026-03-25 231214.png

4. Cross attention

  • 每个vector经过 mask attention 之后产生一个新向量,这个新向量会与Encoder输出的向量序列中的KV进行attention运算,这就是cross attention
    - 每个vector同时进行encoder,同时产生q,同时进行mask attention,再同时进行cross attention,最后同时得到新向量。仅仅是 在enctor内部阶段是并行逻辑,在mask内部阶段是串行逻辑,在cross阶段是并行逻辑
  • 整体上所有的vector都是同时进行处理运算,只不过在不同阶段的处理逻辑有区别,所以只需要研究一个vector的处理逻辑,其余的vector仅仅是按照相同逻辑同时处理
  • 举个例子:一条路有泥巴路、水泥路(并行逻辑)、过桥(串行逻辑),不同的路段有不同过路方式。但是有很多条这样一模一样路,让每个vector同时走。

image.png

Day_5

Task_1 总结Transformer框架

历经5小时,画上此图。现开始从头进行总结,其中不乏有一些新的认识,对上文Day_4的某些知识进行纠错

Transformer.drawio.svg

1. 分词器切片(Tokenizer)

原始文本切片成 token 序列

2. 输入嵌入(Embedding)+ 位置编码(Positional Encoding)

Token Embedding编码,将token转成向量;
Positional Encoding编码,将向量带上位置信息\
将嵌入向量和位置编码拼接成向量序列 这是通过坐标系的分布方式

3. Self-Attention 与 Mult-Head-Attention

(1) 计算流程:多个向量同时被传入编码器,后面以一个向量为例作为讲解。

  • 每一个向量下面都有一列词块数据,然后组成词块数据库:q、k、v;
  • 命名一个向量为a:大量数据训练出的参数,得到Wq、Wk、Wv;
  • a向量下面的查询是q1=Wq· a1 键是k1=Wk·a1 值是v1=wv·a1;
  • 分别点积-计算相关性: 这时,以q1为基准,对所有向量下面的ki进行点积,得到i个点积,计算a1与其他向量之间的相关度;
  • 缩放:注意力分数 为了防止随着维度增大,点积的值越来越大,导致后面soft-max时梯度趋于0;我们对点积的值进行scale,进行缩放(除以 √dk),d是每一个对应k的数量,例如k1是d1,k2是d2;
  • softmax: 将相关性转换为概率分布注意力权重)。计算出的i个点积经过soft-max,相关性转换为概率权重:权重越高的位置,其对应的 Value 对这个a输出贡献越大,让模型能聚焦重要信息。这是一些和为1的小数
  • 与V相乘-聚合value: 每个概率权重的小数与其对应的v进行概率加权,聚合了value,上下文表示,所有的点积 集成一个新向量。
  • 整个流程:以一个向量,得到的多个点积,进过缩放和soft-max,得到概率分布,再通过与v加权,聚合所有value于一个新向量。这个向量有上下文表示。

(2) 为什么要用mult-head-attention?

因为一个向量,他与其他向量的关联是多维度的:语义,语法等多个角度的联系。所以有多个Wq,这个向量与每一个Wq同时并行进行自注意力计算。所以实际情况是每一个a在并行自注意力计算,他们的每一个q也在同时计算,是一个立方体的形式。

4.拼接和线性投影

因为一个向量会同时与12个q进行计算,这个向量最终会得到十二个新向量,我们就要用拼接和投影让他们集成一个新向量。

5.Add+Norm: 残差连接+层归一化

(1)论文:Post-LN;实际:Pre-LN:先Norm 后Add
(2).Add:残差连接:缓解梯度消失
(3).Norm:层归一化:稳定训练

6.FFN:前馈神经网络:

(1)是一个两层的全连接网络--引入非线性和特征变换
(2)非线性: 注意力机制是线性的加权求和,而 FFN 通过激活函数(ReLU/GELU)引入非线性,增强模型的表达能力。
(3)特征变换: 先升维(4 倍)再降维,类似“记忆-召回”机制,让模型在高维空间中学习更丰富的特征组合。

7.Add+Norm

8.循环Block:

第一个Block结束,输出一个新向量,传入下一个Block,循环计算

9.编码器结束:输出向量序列

最后一层Block结束,传出新向量;此时编码器结束,每一个新向量成为向量序列,携带上下文信息。

10.Masked-Mult-Head-Attention: 带掩码的自注意力

(1).开始解码:此时通过一个Bos,编码器的输出向量作为解码器的输入向量,开始运算。

(2).为什么要带掩码? 与Mult-Head-Attention的区别

因为这时是输出,本质是一种预测。不像编码器阶段:用户输入的消息一次性全部接收到,从而可以并行计算。所以我们必须通过串行的方式,递增的与k进行点积:k1---k1、k2---k1、k2、k3····。我们只知道之前的输出,并不知道后面的输出。

11.拼接和线性投影

12.Add+Norm

13.FFN

14.Add+Norm

15.Cross-Attention:与seli-attention的区别

(1) 区别

  • Q:来自 目标序列(如解码器当前时刻的表示)。
  • K、V:来自 源序列(如编码器的输出)对编码器输出的向量序列,投影整合出KV
  • 计算出的注意力权重 表示目标序列的每个位置 应该关注源序列的哪些位置。

(2) 作用

实现“跨序列信息交互”的关键组件, 能关注外部条件,从而胜任复杂的序列到序列任务

16.拼接和线性投影

17.Add+Norm

18.FFN

19.Add+Norm--一个Block结束

20.Block循环

21.输出向量序列

22.线性层 + Softmax输出词概率--词表转换--看见答案

Task_2:区分三种模型

  • Encoder-only(如 BERT):适合理解任务,双向上下文。
  • Decoder-only(如 GPT):适合生成任务,自回归,单向上下文。
  • Encoder-Decoder(如原始 Transformer、T5):适合序列转换任务(如翻译),编码器读入源序列,解码器生成目标序列。

Day_5

Task_1 理解transformer下的大模型和agent的本质

1.transformer是一个完备的架构,可以解决sequence to sequence模式的问题,将一种格式的序列转换成另一种格式序列。
2.agent只是一个运行逻辑框架,按照业务处理的逻辑顺序,最后所有的结果都要化身为提示词交给大模型,让他根据提示词给我们答案。
3.想要智能体变得更智能,首先就是我们要把agent函数的处理逻辑代码写得好,不断的优化他的处理逻辑,提高效率。这是逻辑上
4.大模型一经发布后,他的训练数据就已经定格了,信息不会更新了。
5.你想要大模型认识业务上面的一些专属名词,或者你想让他固定某些行为,你就要对大模型进行微调,定制化处理。这样他就会永久的知道是什么,而不是单一的通过提示词知道。
6.大模型只是一个环节,他不会记住任何东西,一个工具一样,你用他一次不会有任何变化,除非你对他微调,改变他的权重参数,他才会有变化。
7.微调大模型,要么下载开源模型本地部署进行微调,但是要有硬件条件,要自己写代码,微调后可以永久保存调用。要么采用服务商提供的微调服务,你只要把数据给他,他替你微调,替你保存,你只要调用就行,更方便。
8.想要agent记住你的历史几率,或者你告诉他的东西不要每次重复说一遍,就可以建立一个向量数据库和提示词文件(你告诉他的行事格式)。你给agent说的东西,他每次都进向量数据库中检索,或进提示词文件检索,再统一行成最后的提示词交给大模型,这样他就知道了你想要什么,给你什么,所以就有了agent框架。
9.gpt,deepseek是采用decoder-only模式。他的逻辑是:用户输入-agent-decoder-自回归生成一个词拼接到输出序列后面--decoder处理序列-自回归生成一个词拼接到下一个生成序列-decoder······循环-agent-调用工具-agent-decoder-自回归-decoder-自回归-agent-用户想要的结果。
10.这种模式:大模型本身是decoder--自回归生成一个词---decoder---自回归生成一个词----不断循环。
11.加入了agent后: 用户输入-agent-decoder-自回归-agent-工具-agent-decoder-自回归-agent-输出; 多了一层agent调用外面的工具给大模型结果这一步骤; 而agent里面还有他自己分析得到结果的循环模式,再进行agent局部分析。

Task_2 工程化

1.重试机制(tenacity)

  • @retry 装饰器包装函数,在函数抛出异常时,自动重新调用该函数,直到成功或达到最大次数
  • 设置:@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
  • stop=stop_after_attempt(3):最多重试 3 次(加上首次调用,共 4 次尝试)
  • exponential指数避让策略: multiplier=1 × (2 ^ (attempt_number - 1)) 意思是第一次等1秒,第二次等2秒,第三次等4秒;
  • 但是实际上由于设置了最小等待时间2,所以第一次也是等2秒
  • 但是又设置了最大等待时间10,所以最多等10秒
  • 重试机制不仅仅是“再试一次”,而是有策略地、有节制地尝试恢复,这在生产环境中至关重要。

2.日记系统

  • 让终端更干净,显示想要的消息
  • 让日志更详细,记录详细的信息;同时是异步,没有性能影响
  • 可以设置轮转的大小、时间

Task_3 解包

元组解包:action, action_input = extract_action(llm_output)
从 LLM 返回的文本 llm_output中,用正则表达式提取出以 Action: 和 Action Input: 开头的两行内容。

        llm输出:
            Thought: 用户想查询天气
            Action: get_weather
            Action Input: 北京
        
        action 得到工具名称(如 `'get_weather'`)
        action_input 得到工具参数(如 `'北京'`)
        因此:extract_action 会返回元组 ('get_weather', '北京')。
        之后,程序会用 action 去字典 tools 中查找对应的函数,然后用 `action_input` 作为参数调用它。
        
        如果没有action action_input:
            result = extract_action(llm_output)
            action = result[0]
            action_input = result[1]
            
        元组解包让代码更简洁

Day_6

Task_1 学习RAG概念

1.准备阶段:
【切片】文档切片->
索引】Embedding编码成向量->将向量 与 片段文本存入向量数据库
2.使用阶段:
召回】用户问题->Embedding编码成向量->(similarity( ,)向量相似度计算公式)从向量数据库中初筛出相关向量文本->
【重排】(cross-encoder模型)精挑细选->
生成】将用户问题和相似文本拼接后,以文字形式 作为提示词一起发给大模型
综上:只是通过Embedding编码将文档文本和用户提问文本转换成向量,再通过相似度计算找到相似文本,把提问和查到的相似文本拼接作为提示词发给大模型;向量是个媒介

image.png

image.png

Task_2 跑通简单的rag

1.索引阶段:
文档加载:读取本地 .txt 文件(knowledge 文件夹)
文本切块:用 split("\n\n") 按段落切分,每个块成为独立片段
向量化:用嵌入模型(如 all-MiniLM-L6-v2)将每个文本片段转换成固定维度的向量
存储:将向量、原始文本、元数据(如来源文件、块序号)存入向量数据库(Chroma)
2.检索阶段:
查询向量化:用户问题也用同一个嵌入模型转换成向量。
相似性搜索:在向量数据库中计算查询向量与所有存储向量的相似度(默认余弦相似度),找出最相似的 top_k 个片段。
返回文本:将检索到的片段文本返回(通常合并为一个字符串)
3.生成阶段:
构建提示词:将检索到的片段作为上下文,与用户问题一起拼接到提示词中。
调用大模型:让大模型基于这些上下文生成答案
返回结果:输出最终答案
总结:

  1. 最后这也是当成一个工具集成在tool类中,且其中具体实现过程比上述列的更复杂一些;
  2. 向量数据库中是以集合为单位存储数据,**一个集合就像一个表 **
  3. 通过列表推导式把knowledge中的.txt文件 以列表形式存起来,方便检索
  4. 用循环的方式:先读一个文件,然后赋值给text,再用.split。方式把文本切分成块并返回一个列表chunks
  5. 再用循环方式:依次读取chunks列表中的 索引内容 和 值;最后以字典方式将内容、值、元数据(路径)全部存在docs列表中
  6. 循环有两层:一层是循环每个文件,另一层是循环一个文件中的每一块切分文本
  7. 虽然docs是一个列表里面存了三类数据,但是通过读取docs,把一个列表中的三类数据以一个集合的方式存进向量数据库,collection类似是一张表
  8. 检索函数:用collection.query--该方法会自动将用户问题用同一个嵌入模型转换成向量,然后与库中所有向量计算相似度,返回前 top_k 个结果。
  9. 先指定向量数据库路径,再定义collection,指定嵌入函数模型embedding
    优化:将检索阶段单独封装成函数,后续更新时再运行即可。将索引和检索单独分开,不像实操阶段时写在一起测试

Day_7

Task_1 优化rag

1.优化切块策略:使用RecursiveCharacterTextSplitter
2.调整切块参数:在 indexer.py 中修改 chunk_size 和 chunk_overlap,重新索引,测试不同参数。
3.换嵌入模型embedding:更适合中文的
4.重排序:有依赖问题,暂时未使用
5.混合检索:将向量检索和关键测检索融合在一起吗,按综合得分排序,取前几名。

Task_2 设置一个可视化界面

Task_3 weather_tool实时联网

解决了url连接问题,大费周折

Task_4 当前agent总结

目前具备能力:

  • ✅ 真实天气查询(支持任意城市,自动获取 LocationID)
  • ✅ 私有知识库检索(RAG,已测试通过)
  • ✅ 工程化能力(重试、日志、配置管理)
  • ✅ 命令行交互(react_agent.py
  • ✅ Web 界面雏形(Streamlit 基础版)

Day_8

Task_1 整理react_agent框架

image.png

Task_2 整理README.md文件 做详细的回顾

Task_3 逐个文件看代码逻辑

整体框架在readme.md文件中有陈述,看着复杂是因为有很多抛出异常防御性机制,所以优先看readme.md文件中框架

Task_4 部署云端服务器

  1. 整个流程:
    1.买服务器 → 2. SSH 连接 → 3. 上传代码 → 4. 安装依赖 → 5. 设置环境变量 → 6. 开放防火墙端口 → 7. 启动服务(并后台运行)

    | 购买服务器 | 安全组(防火墙)、公网 IP、SSH 端口 22                   |  
    | 登录服务器 | SSH 协议、root 用户、密码/密钥认证                     |  
    | 安装环境  | `apt` 包管理器、Python 虚拟环境(可选)                 |  
    | 上传代码  | `scp` 或 `git clone`、文件权限                   |  
    | 配置服务  | systemd 服务文件、环境变量、工作目录                     |  
    | 开放端口  | 阿里云安全组添加规则、`0.0.0.0/0`                     |  
    | 启动服务  | `systemctl start/enable`、查看日志 `journalctl` |  
    | 更新代码  | `git pull``systemctl restart`
    
  2. 修改代码更新流程:

    1. 在本地修改代码
      本地运行 streamlit run first_agent/app.py 测试,确保功能正常。
    2. 提交到 GitHub git add .
      git commit -m "描述你的修改"
      git push origin main
    3. 在服务器上拉取更新并重启服务 cd /root/ai-agent/ai-agent-learning git pull origin main sudo systemctl restart ai-agent

至此,第一个简单的first_agent项目已结束,换一个帖子开启更高阶的学习。