
摘要:本文通过一个从零构建的本地编程助手Agent项目,详解FunctionCalling如何与ReAct(推理-行动)模式协同工作,实现大模型对文件系统的增删改查操作。包含完整的Python代码解读、工作流程图及核心概念剖析,助你彻底掌握Agent的底层执行逻辑。
2025年开始智能体这个概念异常火热,从上半年的FunctionCalling、MCP到下半年的Skills。这些标准、协议、规范都旨在通过给大模型扩展“手脚”,来实现大模型从对话辅助到实际干活的跨越,经过这一年的发展,CC、OpenCode、Cursor、OpenClaw等等Agent的涌现,及其具备的能力,可谓是发展火热。程序开发效率得以大幅提升,也引发了程序员的生存危机考虑。本文不探究技术层面以外的影响,这系列博客目标是个人AI转型期间的所有经验积累,因此言归正传,回到主题:FunctionCalling理解。
在讲概念之前,先讲一个代码样例(github地址:https://github.com/coder-sswdg/myAIAgent/tree/main),该样例是作者从0开始创建的一个本地编程Agent(因为是编程助手,所以核心就是文件夹、文件的增删改查),大模型从想到干完成的方式是基于ReAct模式通过FunctionCalling调用具体实现函数来实现。
1、代码解释
src/demo
--
|--do_exec_react.py
|--main.py
|--system_prompt.py代码结构目录如上,其中:
a) main.py是Agent入口,运行后启动对话,接收用户输入,然后通过ReAct循环完成任务。
# main.py
"""
主程序入口
启动编程助手 Agent,接收用户输入,运行 ReAct 循环完成文件操作任务
"""
from do_exec_react import run_react_loop
def main():
"""
主函数:启动交互式命令行界面
"""
print("📁 欢迎使用本地编程助手 Agent")
#print("输入您的需求,例如:")
#print(" - 创建一个叫 my_project 的目录")
#print(" - 在 my_project 中创建 hello.py 文件")
#print(" - 读取 hello.py 的内容")
#print(" - 修改 hello.py 添加函数")
#print(" - 删除某个文件")
#print(" - 搜索所有 .py 文件")
print("输入 'quit'、'exit' 或 'bye' 退出程序。\n")
while True:
# 获取用户输入
user_input = input("👉 你:").strip()
# 跳过空输入
if not user_input:
continue
# 退出指令
if user_input.lower() in ['quit', 'exit', 'bye']:
print("👋 再见!祝你编码愉快!")
break
# 运行 ReAct 循环处理请求
result = run_react_loop(user_input)
# 输出最终答案
print(f"\n✅ 助手回复:{result}")
# 程序入口点
if __name__ == "__main__":
main()b) do_exec_react.py是Agent执行核心,它的关键点是路由到大模型处理用户请求,根据大模型处理返回匹配到具体的Function,执行Function将结果送给大模型,大模型进一步思考然后下发下一步指令,直到任务完成。
def run_react_loop(user_input: str, max_steps: int = 10) -> str:
"""
运行 ReAct 循环:思考 → 行动 → 观察 → 再思考……直到得出最终答案
参数:
user_input (str): 用户原始请求
max_steps (int): 最大循环步数,防止无限执行
返回:
str: 最终的答案回复
"""
# 初始化对话上下文
messages=[
{
"role": "system",
"content": SYSTEM_PROMPT
},
{
"role": "user",
"content": user_input
}
]
print("当前消息格式:")
print(json.dumps(messages, ensure_ascii=False, indent=2))
for step in range(max_steps):
print(f"\n🔄 第 {step + 1} 步:生成思考与动作...")
print(f"\n 上下文内容是{messages}")
# 模拟调用 LLM 获取响应(实际项目中替换为此处的真实 API)
agent_response = simulate_llm(messages)
print(f"🤖 Agent:{agent_response}")
# 检查是否已有最终答案
if "<final_answer>" in agent_response:
return extract_final_answer(agent_response)
# 解析动作
action_name, args = parse_action_improved(agent_response)
print(f"parse action is action_name={action_name},args={args}")
if not action_name:
messages = add_message(messages, "assistant", "<observation>⚠️ 未能解析出有效动作。</observation>")
messages = add_message(messages,"user","请根据是否需要创建文件、更新文件、写入文件来重新思考任务")
continue
# 执行对应工具函数
if action_name in TOOLS:
observation = TOOLS[action_name](args)
else:
observation = f"❌ 未知的操作:{action_name}"
print(f"🔍 观察结果:{observation}")
# 将本轮交互追加到上下文中,供后续步骤参考
messages = add_message(messages, "assistant", f"<observation>{observation}</observation>\n")
messages = add_message(messages,"user",f"根据observation结果,进行下一步思考和任务")
# 再次检查是否有 final_answer
if "<final_answer>" in agent_response:
return extract_final_answer(agent_response)
return "<final_answer>⚠️ 达到最大步骤限制,未能完成任务。</final_answer>"这是核心函数,可以发现Agent进入到这一步骤之后,做的动作如下:
第一步是先拿到初始的System Prompt和当前用户的输入请求,这就构成了给大模型的最初始的请求。
这里的System Prompt怎么理解就涉及到提示词工程这个话题,该话题后面再详细展开,大家只需要知道在Agent中System Prompt就是来告诉大模型,你该按照怎样的流程思考,你有哪些工具可以使用,遇到什么关键词就意味着你任务完成了。
第二步循环调用大模型,将最初始请求发送给大模型之后,将大模型的返回agent_response,打印后分析是任务已解决,还是需要继续执行action。
第三步调用对应工具函数```observation = TOOLS[action_name](args) ```让具体的Function干活,比如创建文件,在本样例中支持的Function如下。
- CREATE_FILE(path="...", content="...") → 创建文件(可指定路径和内容)
- READ_FILE(path="...") → 读取文件内容
- UPDATE_FILE(path="...", content="...") → 更新文件内容
- DELETE_FILE(path="...") → 删除文件
- CREATE_DIR(path="...") → 创建目录(支持嵌套)
- DELETE_DIR(path="...") → 删除目录及其内容(递归删除)
- LIST_DIR(path="..." 或 ".") → 列出目录中的子目录和文件
- SEARCH_FILE(pattern="...", path="...") → 在指定路径下按文件名模式搜索(支持正则)第四步这是很关键的一步,这一步是为什么Agent能够迭代完成任务的关键```add message```,将当前大模型的输出叠加到对话上下文中,这样再次将message发给大模型时,大模型不仅能看到初始的系统约束和用户请求,还能看到自己的思考和行动历史,基于已完成动作来进行下一步动作。
"""添加消息到对话列表"""
def add_message(message_list, role, content):
message_list.append({
"role": role,
"content": content
})
return message_list这里说一句题外话,我们看到的模型支持多少上下文窗口,指的就是这个,越大的上下文窗口,意味着模型可以记忆更长的内容,即有完成更复杂任务的可能性。
循环这1~4步直到任务完成。
c) system_prompt.py,从名称就可以看出这里承载的就是Agent的系统提示词,使用FunctionCalling来完成任务,为了避免模型幻觉和内容陈旧,一般都是结合ReAct模式一起来使用,确保任务完成。
2、代码工作流程图说明

从文字介绍和流程图说明,对于Agent的工作流程和工作原理现在已经了解到位了,下来我们进入原理讲解。
3、ReAct模式
ReAct模式是一种让大语言模型通过交替进行“推理(Reasoning)”和“行动(Acting)”来解决复杂任务的高级提示框架。它由谷歌和普林斯顿大学的研究人员在2022年首次提出,旨在将模型的内部思考能力与外部工具调用能力协同起来。核心工作机制:思考-行动-观察 循环。
如果把大语言模型比作一个“大脑”,那么ReAct模式就是为这个大脑装上了能够执行具体操作的“手和脚”,以及能够感知外部反馈的“五官”。
在具体的应用中 ReAct 模式的思考与行动,按照标准标准流程进行:
a. <thought> ... </thought>:先分析当前情况,思考下一步该做什么
b. <action> ... </action>:调用一个可用工具执行具体操作
c. 等待环境返回 <observation> ... </observation>:观察执行结果
d. 根据观察继续下一轮思考或给出最终回答
4、FunctionCalling
从上面例子已经可以明确FunctionCalling就是Agent解析大模型的返回后,调用Agent自身支持的一些列函数,来实现与外部世界(一般就是电脑)的交互,完成具体的任务,是具体的动作执行。
理解了这一点,也就可以理解为什么同样的功能,针对不同的Agent都需要挨个实现一遍,因为Function就在Agent内部。
5、其他
Agent本质是通过大模型的API实现多轮对话,那么在对话中有几个角色概念,需要了解一下。
角色:system,这个代表接下来内容是系统提示词。
角色:user,这个代表相关内容是用户输入。
角色:assistant,这个代表相关内容是大模型的输出。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。