基于FunctionCalling与ReAct范式构建文件归类助手:定义list_files和move_file工具,构建带最大轮次防护的ReAct循环,自动读取移动文件,并维护消息列表保持AI记忆,实现文件智能分类。
上两节课我们学习了关于Function Calling和【思维范式】的各种理论知识和思路。今天直接开始实战——目标是做一个可以自动归类文件的AI助手。先看看使用效果:

长期稳定更新的攒劲资源: >>>点此立即查看<<<
要做到这一点,至少得满足两个条件:AI能准确知道本地文件夹有哪些文件;AI能精准操控(移动)本地的文件。这恰恰是Function Calling最核心的价值所在。而调用这些Function,则属于【思维范式】的领域。
为了让实践更有参与感,又不影响日常工作,写了一个脚本,可以一键生成一个混乱无比的文件夹。

执行脚本就能生成这样一个混乱的文件夹。

当然,这些文件都没有内容,只是为了模拟更真实的职场环境。一键生成模拟文件的脚本可以在demo工程里获取,执行以下命令即可:
python .lesson_06generate_files.py
这个脚本和本节课关系不大,就不展开了。
上节课提到了ReAct思维范式——让AI按“思考→行动→检查→思考→行动→……”的方式行动。但现在的LLM底层基本都内置了这种范式,很多时候甚至不需要特意用提示词去要求,它们默认就是ReAct的。
接下来,先梳理清楚解决这个问题需要分成几个步骤。

如上图,核心有三个步骤:提供方法让AI能读取文件夹内的所有文件;提供方法让AI能移动文件夹内的文件;构建ReAct循环,直到AI完成整个任务。
这两个函数对任何能操作文件系统的编程语言来说都不算困难。首先是list_files方法,核心代码很简单:
for f in os.listdir(BASE_PATH):
f_path = os.path.join(BASE_PATH, f)
# 过滤逻辑:只看文件,忽略隐藏文件(.开头)
if os.path.isfile(f_path) and not f.startswith('.'):
files.append(f)
# 必须返回 JSON 字符串,而不是 Python 列表
return json.dumps({"files": files}, ensure_ascii=False)
稍微注意一点:最后返回的不能是数组,而是JSON字符串。然后是move_file函数:
# 检查源文件
if not os.path.exists(source_file):
return json.dumps({"error": f"文件不存在: {filename}"}, ensure_ascii=False)
# 检查并创建目标目录
if not os.path.exists(target_folder):
os.makedirs(target_folder)
# 移动文件
shutil.move(source_file, target_file)
也是非常容易,几乎没什么特别要注意的,返回结果时序列化成JSON字符串即可。
这一步要构建一个【工具清单】,告诉AI你的本地能提供哪些方法,分别叫什么名字、有什么用、接收哪些参数。

这样,当AI遇到困境时,才会想起调用你提供的方法。
# 工具定义 (供 LLM 阅读)
tools_schema = [
{
"type": "function",
"function": {
"name": "list_files",
"description": "查看当前文件夹里有哪些文件待处理。",
"parameters": {"type": "object", "properties": {}}
}
},
{
"type": "function",
"function": {
"name": "move_file",
"description": "将指定文件移动到目标文件夹中。",
"parameters": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "源文件名 (必须是 list_files 返回列表中存在的名字)"
},
"category": {
"type": "string",
"description": "目标文件夹名称 (例如 'Images', '合同文件', '简历')"
}
},
"required": ["filename", "category"]
}
}
}
]
这块细节在本专栏第五课《春哥的Agent通关秘籍05:工具调用 Function Calling【知识与思路篇】》中有详细说明,实战篇就不赘述了。
ReAct循环的核心是一个while循环——把整个交互包裹在while True里,只要AI还想调工具,就一直陪它跑下去,直到它认为信息足够、不再调用工具为止。

上图按照Agent最常见的while循环逻辑画出了示意图。关键除了最大轮次判断之外,最需要注意的是这里:

判断AI是否有工具调用的诉求。如果没有,直接反馈给用户;否则进入while循环。因此编写while循环版本时,要引入两个重要机制:MAX_ROUND防护,防止AI死循环烧光你的钱;if not ai_msg.tool_calls时跳出循环,返回final_result。
核心代码如下:
MAX_TURNS = 60
for turn in range(MAX_TURNS):
print(f" 第 {turn+1} 轮思考中...")
# 1. 呼叫大模型
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=tools_schema
)
以及对response内容的检查:
if ai_message.tool_calls:
print(f" 触发了 {len(ai_message.tool_calls)} 个操作请求!")
# 3. 遍历并执行所有工具调用 (Parallel Function Calling)
for tool_call in ai_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
# 略:根据方法名和参数调用方法
else:
# 如果没有工具调用,说明任务结束,AI 给出了总结
print(ai_message.content)
break
众所周知,LLM没有记忆——每次聊天都是一次全新的相遇。打个比方:你看到小美,上去打招呼“小美你好,我是李雷”;小美回复“李雷你好,我是小美”;你马上问“你复述一遍,我是谁?”小美会说“抱歉,我不知道你是谁,这是我们第一次聊天”。
为了解决这个问题,人们想到一个简单实用的方法:这次你递过去一个清单,上面记载了你们刚才的聊天记录,然后问“你复述一遍,我是谁?”小美就能回答“当然,你是李雷”。
没错,你现在用到的看起来有记忆的LLM聊天工具,其实每一轮会话都会把所有聊天记录都塞进去。

这是所有Agent开发者都绕不开的难题——每次会话的聊天清单越来越长,当token达到一定长度后,就会带来收费、响应速度、降智、超出最大长度等一系列问题。这里先按下不表,但必须知道,需要永远按以下方式维护message来让LLM保持记忆:
在demo工程中依次执行以下指令:
python .lesson_06generate_files.pypython .lesson_06ai_organizer.py
执行后:
本节用前两节课的知识点进行了一次简单实操——实现本地的Function作为tools传递给AI、在本地构建ReAct循环、维护messages清单以保持AI记忆。下节课将深入理解AI的本质,学习什么是向量、什么是向量化,以及如何构建一个本地知识库,让AI能够快速检索你的私有知识。
敬请期待!

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述