项目01-手搓Agent之loop

1 介绍

loopAgent 实现最重要的一个 part,其中分为 lead agent 的 loop 和 sub_agent 的 loop

2 代码-接收控制台输入的命令

python 复制代码
def interactive_mode():
    """🎮 启动交互式命令行模式"""
    # 打印Banner
    print_banner()
    # 打印使用提示
    print_info("输入任务描述,或输入 [bold]quit[/] 退出")
    print_info("使用 [bold]--plan[/] 前缀启用任务规划模式")
    console.print()
    
    # 循环接收任务
    while True:
        try:
            user_input = get_user_input(">>")
            # 退出命令
            if user_input.lower() in ['quit', 'exit', 'q']:
                console.print("\n[bold cyan]👋 感谢使用 EasyAgent[/]\n"); break
            if not user_input.strip():
                continue
            
            # 检测是否启用任务规划
            use_plan = user_input.startswith("--plan ")
            task = user_input.replace("--plan ", "").strip()
            if not task:
                print_error("请输入有效的任务描述"); continue
            
            console.print()
            # 执行智能体
            run_agent_plus(task, use_plan=use_plan)
            console.print()
        except KeyboardInterrupt:
            console.print("\n[yellow]⚠️  任务中断[/]"); continue
        except Exception as e:
            print_error(f"执行异常: {e}"); continue
  • 第一步: 边界条件的判断,如果输入包含 quit 等退出指令,则打印提示,并 break 循环 loop;
  • 第二步: 判断用户是否使用任务分解 --plan ,如果使用的话,作为智能体执行 run_agent_plus() 的参数;

3 代码-Agent的入口

python 复制代码
def run_agent_plus(task, use_plan=False, keep_chatting=False, use_team=False):
    """增强版智能体入口"""
    # 加载历史记忆
    memory = load_memory()
    system_prompt = "您是一位能够与系统交互的得力助手。请言简意赅。"
    # 拼接记忆到系统提示
    if memory:
        system_prompt += f"\n\n之前的上下文:\n{memory}"
    messages = [{"role": "system", "content": system_prompt}]

    # 使用 Agent 团队处理(如果启用)
    if use_team and agent_team:
        print_section("🤖 使用 Agent 团队处理", style=THEME["primary"])
        task_id = agent_team.submit_task({"content": task, "description": task})
        print_info(f"任务已提交到队列: {task_id}")
        return f"Task {task_id} submitted to team"

    # 生成任务步骤
    steps = create_plan(task) if use_plan else [task]
    print_section("🚀 开始执行任务", style=THEME["success"])
    
    all_results, step_index = [], 0
    
    # 遍历执行所有步骤
    while step_index < len(steps):
        step_index += 1
        current_step = steps[step_index - 1]
        
        # 多步骤时显示当前步骤面板
        if len(steps) > 1:
            console.print(Panel(f"[bold]{current_step}[/]", title=f"[{THEME['primary']}]📍 步骤 {step_index}/{len(steps)}[/]", border_style=THEME["border"], padding=(1, 2)))
        
        # 执行单步任务
        result, actions, messages = run_agent_step(current_step, messages)
        all_results.append(result)
        print_result(result, title=f"✨ 步骤 {step_index} 执行完成")
        
        # 获取用户反馈
        feedback, should_continue = get_step_advice(step_index, len(steps), current_step, result)
        
        # 处理反馈指令
        if feedback == "__skip_all__":
            console.print("[yellow]⏭️  跳过剩余步骤,任务提前结束[/]"); break
        # 处理控制指令
        if feedback == "__retry__":
            print_info(f"🔄 重新执行步骤 {step_index}...")
            step_index -= 1  # 回退索引,重试当前步骤
            continue
        elif feedback == "confirmed":
            # 用户确认,无需额外操作,继续下一步
            pass
        elif feedback:  # 非空 = 用户建议,直接透传给 AI
            console.print(Panel("[dim]🔄 正在处理您的建议...[/]", border_style=THEME["primary"], padding=(0, 2)))
            messages.append({"role": "user", "content": f"[步骤反馈] {current_step}\n用户建议: {feedback}"})
            
            response = with_spinner("🤖 思考中...", client.chat.completions.create, 
                                model=MODEL_NAME, messages=messages, tools=TOOLS)
            ai_response = response.choices[0].message.content
            
            # 展示 AI 响应
            if ai_response:
                print_result(ai_response, title=f"💬 响应")
                messages.append({"role": "assistant", "content": ai_response})
        
        if not should_continue:
            break
    
    # 合并所有结果并保存记忆
    final_result = "\n".join(all_results)
    save_memory(task, final_result)
    console.print(Panel("[bold green]🎉 任务执行完成![/]", border_style=THEME["success"], padding=(1, 2)))
    
    # 开启连续对话
    if keep_chatting:
        messages = continue_chatting(messages, task)
    return final_result

第一部分:加载历史记忆

这里我们用的是 .md 存储用户以往的对话,对话内容包含 QueryResultsummary之后的内容,所以 Token 消耗并不大),具体操作就是简单的拼接,然后以 System 的方式封装到 messages 列表中:

python 复制代码
		memory = load_memory()
    system_prompt = "您是一位能够与系统交互的得力助手。请言简意赅。"
    # 拼接记忆到系统提示
    if memory:
        system_prompt += f"\n\n之前的上下文:\n{memory}"
    messages = [{"role": "system", "content": system_prompt}]

第二部分:是否使用 Agent Team

如果使用 Agent Team,那么任务就提交到任务队列中,字段包含 task_iddescription 等(顶层任务无依赖方);

python 复制代码
 		# 使用 Agent 团队处理(如果启用)
    if use_team and agent_team:
        print_section("🤖 使用 Agent 团队处理", style=THEME["primary"])
        task_id = agent_team.submit_task({"content": task, "description": task})
        print_info(f"任务已提交到队列: {task_id}")
        return f"Task {task_id} submitted to team"

第三部分:任务分解

本质上,就是通过提示词的方式,指定 response_format 让大模型生成 json 结构的分解步骤:

python 复制代码
		# 生成任务步骤
    steps = create_plan(task) if use_plan else [task]
    print_section("🚀 开始执行任务", style=THEME["success"])
  • 如果设置任务分解,这里会是第一次问答;

第四部分:回答每个 step 任务

返回最终的结果,其中 result 是模型对一个 Step 最后一次回答的结果(完成一个 Step 至少需要回答两次);

actions 的内容是所有执行的工具的 nameargs
messages 列表中包含了 tools_resultmodel_response

python 复制代码
		all_results, step_index = [], 0
    
    # 遍历执行所有步骤
    while step_index < len(steps):
        step_index += 1
        current_step = steps[step_index - 1]
        
        # 多步骤时显示当前步骤面板
        if len(steps) > 1:
            console.print(Panel(f"[bold]{current_step}[/]", title=f"[{THEME['primary']}]📍 步骤 {step_index}/{len(steps)}[/]", border_style=THEME["border"], padding=(1, 2)))
        
        # 执行单步任务
        result, actions, messages = run_agent_step(current_step, messages)
        all_results.append(result)
        print_result(result, title=f"✨ 步骤 {step_index} 执行完成")

单步骤执行的具体逻辑 run_agent_step()

    1. 先对任务进行请求,提取 tool_calling 字段看需要调用的工具内容和参数;
    1. 注入工具,再进行问答,封装到 messages 列表中;
    1. 工具,可以通过懒加载先把元数据加载到 system_prompt 中,然后在具体使用具体某个 tool 的时候再加载详细的描述;
python 复制代码
# ===================== 单步执行 =====================
def run_agent_step(task, messages, max_iterations=5):
    """执行单个步骤"""
    # 将当前任务加入对话上下文
    messages.append({"role": "user", "content": task})
    # 存储执行的动作
    actions = []
    
    # 最大迭代次数,防止死循环
    for iteration in range(max_iterations):
        if iteration > 0:
            print_info(f"第 {iteration + 1} 次迭代...")
        
        # 调用AI,带加载动画
        response = with_spinner("AI 思考中...", client.chat.completions.create, model=MODEL_NAME, messages=messages, tools=TOOLS)
        message = response.choices[0].message
        
        # 将AI回复加入上下文
        messages.append({
            "role": message.role,
            "content": message.content,
            "tool_calls": message.tool_calls if hasattr(message, "tool_calls") else None
        })
        
        # 无工具调用,直接返回结果
        if not message.tool_calls:
            result = message.content if message.content else "[dim](任务已完成,无额外输出)[/]"
            return result, actions, messages
        
        # 遍历所有工具调用
        for tool_call in message.tool_calls:
            function_payload = getattr(tool_call, "function", None)
            if function_payload is None:
                continue
            # 获取函数名和参数
            function_name = str(getattr(function_payload, "name", ""))
            raw_arguments = str(getattr(function_payload, "arguments", ""))
            function_args = parse_tool_arguments(raw_arguments)
            
            # 打印工具调用信息
            print_tool_call(function_name, function_args)
            
            # 查找并执行对应函数
            function_impl = AVAILABLE_FUNCTIONS.get(function_name)
            if function_impl is None:
                function_response = f"Error: Unknown tool '{function_name}'"
            elif "_argument_error" in function_args:
                function_response = f"Error: {function_args['_argument_error']}"
            else:
                function_response = function_impl(**function_args)
                actions.append({"tool": function_name, "args": function_args})
            
            # 将工具执行结果加入上下文
            messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": function_response})
    
    # 达到最大迭代次数,终止执行
    print_error("达到最大迭代次数,终止执行")
    return "Max iterations reached", actions, messages

第五部分:对每个 step 的结果提出建议

  • get_step_advice:从控制台读取 user_input,作为 messages 列表的输入;
  • 第四次问答,模型将根据用户的建议,对 step 重新回答一次;
python 复制代码
# 获取用户反馈
        feedback, should_continue = get_step_advice(step_index, len(steps), current_step, result)
        
        # 处理反馈指令
        if feedback == "__skip_all__":
            console.print("[yellow]⏭️  跳过剩余步骤,任务提前结束[/]"); break
        # 处理控制指令
        if feedback == "__retry__":
            print_info(f"🔄 重新执行步骤 {step_index}...")
            step_index -= 1  # 回退索引,重试当前步骤
            continue
        elif feedback == "confirmed":
            # 用户确认,无需额外操作,继续下一步
            pass
        elif feedback:  # 非空 = 用户建议,直接透传给 AI
            console.print(Panel("[dim]🔄 正在处理您的建议...[/]", border_style=THEME["primary"], padding=(0, 2)))
            messages.append({"role": "user", "content": f"[步骤反馈] {current_step}\n用户建议: {feedback}"})
            
            response = with_spinner("🤖 思考中...", client.chat.completions.create, 
                                model=MODEL_NAME, messages=messages, tools=TOOLS)
            ai_response = response.choices[0].message.content
            
            # 展示 AI 响应
            if ai_response:
                print_result(ai_response, title=f"💬 响应")
                messages.append({"role": "assistant", "content": ai_response})
        
        if not should_continue:
            break

第六部分:存储记忆

将第三次问答的 response 存储到 .md 中;

python 复制代码
		# 合并所有结果并保存记忆
    final_result = "\n".join(all_results)
    save_memory(task, final_result)
    console.print(Panel("[bold green]🎉 任务执行完成![/]", border_style=THEME["success"], padding=(1, 2)))
    
    # 开启连续对话
    if keep_chatting:
        messages = continue_chatting(messages, task)
    return final_result

总结

第 1 次问答: 任务分解,假设分为 n 个子任务(1次问答);
第 2 ~ n+1 次问答: 查看每个子任务需要调用的工具(n次问答);
第 n+2 ~ 2n+2 次问答: 每次调用完工具至少还需要一次问答生成答案总结(n次问答) ;
第 2n+3 ~ 3n+3 次问答: 每个子任务可以提出建议(n次问答);

一共 3n+1 次问答,如果你的任务很复杂,那么单 Agent 会堵死去。这也是为什么要用懒加载和任务规划的原因;

相关推荐
亲亲小宝宝鸭2 小时前
Ctrl ACV工程师的提效之路:删掉项目中的冗余
前端
kyriewen2 小时前
DOM树与节点操作:用JS给网页“动手术”
前端·javascript·面试
米饭同学i2 小时前
基于腾讯云COS的小程序素材上传功能实现
前端·javascript·react.js
cxxcode2 小时前
前端性能指标接入 Prometheus 技术方案
前端
辣椒炒代码2 小时前
🚀 AI Agent 入门实战:基于 LangChain + MCP 构建智能导游助手
前端
郝学胜-神的一滴2 小时前
【技术实战】500G单行大文件读取难题破解!生成器+自定义函数最优方案解析
开发语言·python·程序人生·面试
ruanCat2 小时前
前端工程化工具链从零配置:simple-git-hooks + lint-staged + commitlint
前端·git·代码规范
愤豆2 小时前
02-Java语言核心-语法特性-注解体系详解
java·开发语言·python
光影少年2 小时前
如何开发一个CLI工具?
javascript·测试工具·前端框架·node.js