深度解析 LLM Agent ReAct 架构:基于 Function Calling 的代码重构实践
1. 背景为什么我们需要 Agent痛点传统编程中简单的重复性工作如加注释、改格式浪费了开发者大量时间。思路既然 LLM大模型懂代码能不能让它自己读取文件、修改文件、再保存方案利用ReAct推理行动模式让 LLM 拥有“双手”工具实现全自动闭环。2. 核心架构ReAct 是什么ReAct Reasoning思考 Acting行动流程感知用户下达指令“给代码加注释”。思考LLM 决定第一步要调用read_file。行动Python 代码执行读取文件操作。观察将文件内容返回给 LLM。循环LLM 看到内容决定调用write_file写入新代码。结束任务完成。3. 核心代码实现第一步定义工具# Agent 的“手”用来操作文件 def read_file(file_path): 读取文件内容 try: with open(file_path, r, encodingutf-8) as f: return f.read() except FileNotFoundError: return 错误文件未找到 def write_file(file_path, content): 写入文件内容 with open(file_path, w, encodingutf-8) as f: f.write(content) return 文件写入成功3.1.1 结构拆解从定义到参数拆解一下read_file的 Schema 结构{type: function, // 告诉模型这是一个可调用的函数不是一段文本function: {name: read_file, // 模型决策后返回这个名字你的 Python 代码靠它找到对应的函数description: ..., // 告诉模型这个工具是干嘛的最关键下文细说parameters: { // 定义输入数据的格式type: object,properties: { // 具体参数列表file_path: {type: string, // 类型约束必须是字符串description: 文件路径 // 参数说明告诉模型这里该填什么}},required: [file_path] // 必填项如果不填工具就无法运行}}}3.1.2 为什么description是灵魂很多初学者容易忽略description字段甚至直接留空。但实际上description是决定 Agent 智能程度的关键。LLM 是基于语义理解的它不会去读你的 Python 源代码。它判断“现在该不该调用这个工具”的唯一依据就是description里的文字。一个优秀的description必须具备两点明确触发条件告诉模型在什么情况下使用它。清晰的功能边界告诉模型用了它会有什么效果。❌糟糕的写法description: 写文件此时模型可能会困惑——“是覆盖写还是追加写是写二进制还是文本如果我把路径填错了怎么办”模糊的描述会导致模型犹豫不决甚至乱传参数。优秀的写法description: 将内容写入指定文件用于保存修改后的代码模型不仅知道了用途写文件还明确了场景保存修改后的代码。当用户说“帮我修复这个 Bug”时模型能迅速通过“保存”这个意图匹配到该工具。3.1.3 参数描述的精准性不仅是函数描述参数的description同样重要。看write_file的参数定义content: {type: string, description: 要写入的内容}这告诉模型这个参数是一个字符串。如果你的描述是要写入的字节流模型可能会尝试把文本转成 bytes导致你的程序报错。如果你的描述是修改后的完整代码模型就会倾向于把整个文件内容传进来而不是只传修改的一小行从而保证了写入逻辑的完整性。第二步实现 ReAct 主循环4. 避坑指南坑点一Pydantic 对象与字典的冲突现象在第二轮循环报错AttributeError: CompletionMessage object has no attribute get。原因SDK 底层期望传入字典但message是对象直接append导致类型不匹配。解决方案修复代码model_dump或dict()转换。代码片段msg_dict message.model_dump(exclude_unsetTrue)坑点二上下文丢失原因忘记把“模型之前的决策”存回messages。后果模型在第二轮会“失忆”不知道自己刚才干了什么。解决方案强调必须先append(message)再append(tool_result)。5. 实战演示test.py修改前后的对比修改前修改后