【AiAgent学习】状态管理与多步推理

上一章介绍的ReAct是一次的流程:提问、AI判断是否使用工具、调用、最后返回结果,但是任务如果是复杂的,并不是单一的话,就会使用到状态管理这个概念,需要记住一些状态,比如:规划一个新疆自驾游,需要考虑天气、自驾的路线、热门景点以及住宿等信息,需要进行多次判断,下面我们进入学习。

1.复杂问题步骤

1.1拆解

我们可以想一个复杂的流程问题,我们就拿上面说的问题为例:帮我规划一下去新疆北疆自驾游,大概10天左右,需要知道自驾路线、天气、景点以及每天的住宿信息。

首先第一步,我们写一个计划的函数将这个问题拆开处理

python 复制代码
def plan(self, task: str) -> list:
        """规划:将任务拆解成多个步骤"""
        
        plan_prompt = f"""
你是一个专业的自驾游规划专家。请将以下任务拆解成具体的执行步骤。

任务:{task}

具体要求:
- 任务必须包含自驾游的路线、天气、景点以及每天的住宿信息。
- 任务必须包含至少10天的自驾游时间。
- 地点:新疆北疆。
- 还可以加上推荐的当地美食、活动等。

可用工具:
- get_weather_xj(city): 查天气
- get_time(): 查当前时间
- search_attractions(city): 查景点

请输出JSON格式的步骤列表,每个步骤包含:
- step_id: 步骤编号
- tool: 使用的工具名
- params: 工具参数
- description: 步骤描述

示例输出:
[
    {{"step_id": 1, "tool": "get_weather_xj", "params": {{"city": "乌鲁木齐"}}, "description": "查询乌鲁木齐天气"}},
    {{"step_id": 2, "tool": "search_attractions", "params": {{"city": "可可托海"}}, "description": "查询可可托海景点"}}
]

如果不需要工具,返回:
[{{"step_id": 1, "tool": "answer", "params": {{}}, "description": "直接回答"}}]

请输出:
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": plan_prompt}],
            temperature=0.3
        )
        
        content = response.choices[0].message.content
        
        # 提取 JSON
        import re
        json_match = re.search(r'\[.*\]', content, re.DOTALL)
        if json_match:
            try:
                steps = json.loads(json_match.group())
                return steps
            except:
                pass
        
        # 默认返回
        return [{"step_id": 1, "tool": "answer", "params": {}, "description": "直接回答"}]

这部分主要核心点就在于提示词的描述,添加了几个我们用过的工具函数:

python 复制代码
    def get_weather_xj(self, city: str) -> str:
        """查天气"""
        weather_db = {
            "乌鲁木齐": "晴天,25°C",
            "可可托海": "多云,22°C",
            "天山": "晴天,20°C",
            "布尔津": "阵雨,24°C",
            "喀纳斯": "晴天,24°C",
            "阿勒泰": "晴天,21°C",
            "克拉玛依": "晴天,22°C",
            "赛里木湖": "晴天,24°C",
            "伊宁": "晴天,22°C",
            "那拉提": "阴天,15°C",
            "巴音鲁克镇": "阵雨,18°C",
            "奎屯市": "小雨,14°C",
        }
        return weather_db.get(city, f"{city}:晴天,20°C")
    
    def get_time(self) -> str:
        """查时间"""
        return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
    
    def search_attractions(self, city: str) -> str:
        """查景点"""
        attractions = {
            "北京": ["故宫", "长城", "天坛"],
            "上海": ["外滩", "东方明珠", "迪士尼"],
            "广州": ["广州塔", "长隆", "沙面"],
            "乌鲁木齐": ["天山天池", "国际大巴扎", "红山公园"],
            "阿勒泰": ["喀纳斯湖", "禾木村", "白哈巴村"],
            "布尔津": ["五彩滩", "额尔齐斯河", "布尔津夜市"],
            "伊宁": ["那拉提草原", "赛里木湖", "薰衣草庄园"],
            "克拉玛依": ["魔鬼城", "黑油山", "艾里克湖"]
        }
        return ", ".join(attractions.get(city, ["暂无推荐"]))

我们还可以添加其他的工具类,但是我们要明白天家工具类的作用,就是有些实时信息没法查到,理论上我们要去请求一个比如时间的API然后得到结果数据,所以这个工具类的添加根据自己的具体情况,比如我们还可以加住宿的工具,关于是否需要添加,AI是如下回答的,总结下面回答就是:提供了工具的调用,可以提高AI回答的准确性和效率

1.2 拆开执行

我们第一步拆开了许多任务,第二步就是去执行,拿到结果,主要是循环plan函数返回的数据,包括到哪一步了,描述,用的工具等信息,然后该用工具的用设置好的工具,不用的直接回答,就比如饮食,路线等

python 复制代码
print("\n🔧 步骤2: 执行步骤...")
        
        for step in steps:
            step_id = step.get("step_id")
            description = step.get("description", "")
            tool = step.get("tool", "")
            
            print(f"\n   📍 执行步骤 {step_id}: {description}")
            print(f"      工具: {tool}")
            
            # 执行
            result = self.execute_step(step)
            
            if result:
                print(f"      结果: {result[:100]}...")
                # 保存中间结果
                self.state["intermediate_results"][f"step_{step_id}"] = result
                self.state["completed_steps"].append({
                    "step_id": step_id,
                    "description": description,
                    "result": result
                })
            else:
                print(f"      无需工具,直接回答")

1.3 总结输出

python 复制代码
        print("\n💭 步骤3: 思考总结...")
        conclusion = self.think_next(
            task,
            self.state["completed_steps"],
            self.state["intermediate_results"]
        )
        
        if conclusion.get("is_complete"):
            final_answer = conclusion.get("final_answer", "任务完成")
        else:
            # 如果未完成,让 AI 基于中间结果生成回答
            final_answer = self.generate_final_answer(task, self.state["intermediate_results"])
        
        self.state["final_answer"] = final_answer
        
        return final_answer
    
    def generate_final_answer(self, task: str, results: dict) -> str:
        """基于中间结果生成最终回答"""
        
        prompt = f"""
原始任务:{task}

收集到的信息:
{json.dumps(results, ensure_ascii=False, indent=2)}

请根据以上信息,给出一个完整、友好的回答。
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": prompt}]
        )
        
        return response.choices[0].message.content

中间思考过程看回答有没有完成,是否要进行下一步,直接看提示词就好,先判断是否完成,数据来源于上一步请求提问以及调用工具的结果:

python 复制代码
def think_next(self, task: str, completed_steps: list, results: dict) -> dict:
        """思考下一步:判断是否需要继续"""
        
        think_prompt = f"""
原始任务:{task}

已完成的步骤:
{json.dumps(completed_steps, ensure_ascii=False, indent=2)}

中间结果:
{json.dumps(results, ensure_ascii=False, indent=2)}

请判断:
1. 任务是否已经完成?
2. 如果完成,给出最终答案
3. 如果未完成,说明还需要什么

输出JSON格式:
{{"is_complete": true/false, "final_answer": "最终答案(如果完成)", "next_action": "下一步说明(如果未完成)"}}
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": think_prompt}],
            temperature=0.1
        )
        
        content = response.choices[0].message.content
        
        # 提取 JSON
        import re
        json_match = re.search(r'\{.*\}', content, re.DOTALL)
        if json_match:
            try:
                return json.loads(json_match.group())
            except:
                pass
        
        return {"is_complete": True, "final_answer": "处理完成", "next_action": ""}

2.运行结果

整个计划其实是不错的,因为我去过一次新疆自驾,走的路线时间和设计的差不多,很棒的计划。

3.代码

python 复制代码
from openai import OpenAI
import os
import json
from datetime import datetime
from dotenv import load_dotenv

load_dotenv()

class StatefulAgent:
    """带状态管理的 Agent - 支持多步推理"""
    
    def __init__(self):
        self.client = OpenAI(
            api_key=os.getenv("DEEPSEEK_API_KEY"),
            base_url="https://api.deepseek.com"
        )
        # 状态管理
        self.reset()
    
    def reset(self):
        """重置 Agent 状态(开始新任务时调用)"""
        self.state = {
            "task": None,                    # 当前任务
            "step": 0,                       # 当前步骤数
            "completed_steps": [],           # 已完成的步骤
            "pending_steps": [],              # 待完成的步骤
            "intermediate_results": {},      # 中间结果
            "final_answer": None,            # 最终答案
            "max_steps": 10                  # 最大步数限制
        }
    
    # ========== 工具定义 ==========
    
    def get_weather(self, city: str) -> str:
        """查天气"""
        weather_db = {
            "北京": "晴天,25°C",
            "上海": "多云,22°C",
            "广州": "阵雨,28°C",
        }
        return weather_db.get(city, f"{city}:晴天,20°C")
    
    def get_weather_xj(self, city: str) -> str:
        """查天气"""
        weather_db = {
            "乌鲁木齐": "晴天,25°C",
            "可可托海": "多云,22°C",
            "天山": "晴天,20°C",
            "布尔津": "阵雨,24°C",
            "喀纳斯": "晴天,24°C",
            "阿勒泰": "晴天,21°C",
            "克拉玛依": "晴天,22°C",
            "赛里木湖": "晴天,24°C",
            "伊宁": "晴天,22°C",
            "那拉提": "阴天,15°C",
            "巴音鲁克镇": "阵雨,18°C",
            "奎屯市": "小雨,14°C",
        }
        return weather_db.get(city, f"{city}:晴天,20°C")
    
    def get_time(self) -> str:
        """查时间"""
        return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
    
    def search_attractions(self, city: str) -> str:
        """查景点"""
        attractions = {
            "乌鲁木齐": ["天山天池", "国际大巴扎", "红山公园"],
            "阿勒泰": ["喀纳斯湖", "禾木村", "白哈巴村"],
            "布尔津": ["五彩滩", "额尔齐斯河", "布尔津夜市"],
            "伊宁": ["那拉提草原", "赛里木湖", "薰衣草庄园"],
            "克拉玛依": ["魔鬼城", "黑油山", "艾里克湖"]
        }
        return ", ".join(attractions.get(city, ["暂无推荐"]))

    
    # ========== 核心方法 ==========
    
    def plan(self, task: str) -> list:
        """规划:将任务拆解成多个步骤"""
        
        plan_prompt = f"""
你是一个专业的自驾游规划专家。请将以下任务拆解成具体的执行步骤。

任务:{task}

具体要求:
- 任务必须包含自驾游的路线、天气、景点以及每天的住宿信息。
- 任务必须包含至少10天的自驾游时间。
- 地点:新疆北疆。
- 还可以加上推荐的当地美食、活动等。

可用工具:
- get_weather_xj(city): 查天气
- get_time(): 查当前时间
- search_attractions(city): 查景点

请输出JSON格式的步骤列表,每个步骤包含:
- step_id: 步骤编号
- tool: 使用的工具名
- params: 工具参数
- description: 步骤描述

示例输出:
[
    {{"step_id": 1, "tool": "get_weather_xj", "params": {{"city": "乌鲁木齐"}}, "description": "查询乌鲁木齐天气"}},
    {{"step_id": 2, "tool": "search_attractions", "params": {{"city": "可可托海"}}, "description": "查询可可托海景点"}}
]

如果不需要工具,返回:
[{{"step_id": 1, "tool": "answer", "params": {{}}, "description": "直接回答"}}]

请输出:
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": plan_prompt}],
            temperature=0.3
        )
        
        content = response.choices[0].message.content
        
        # 提取 JSON
        import re
        json_match = re.search(r'\[.*\]', content, re.DOTALL)
        if json_match:
            try:
                steps = json.loads(json_match.group())
                return steps
            except:
                pass
        
        # 默认返回
        return [{"step_id": 1, "tool": "answer", "params": {}, "description": "直接回答"}]
    
    def execute_step(self, step: dict) -> str:
        """执行单个步骤"""
        tool = step.get("tool", "answer")
        params = step.get("params", {})
        
        if tool == "get_weather_xj":
            city = params.get("city", "")
            return self.get_weather_xj(city)
        elif tool == "get_time":
            return self.get_time()
        elif tool == "search_attractions":
            city = params.get("city", "")
            return self.search_attractions(city)
        elif tool == "answer":
            # 直接回答,不需要工具
            return None
        else:
            return f"未知工具: {tool}"
    
    def think_next(self, task: str, completed_steps: list, results: dict) -> dict:
        """思考下一步:判断是否需要继续"""
        
        think_prompt = f"""
原始任务:{task}

已完成的步骤:
{json.dumps(completed_steps, ensure_ascii=False, indent=2)}

中间结果:
{json.dumps(results, ensure_ascii=False, indent=2)}

请判断:
1. 任务是否已经完成?
2. 如果完成,给出最终答案
3. 如果未完成,说明还需要什么

输出JSON格式:
{{"is_complete": true/false, "final_answer": "最终答案(如果完成)", "next_action": "下一步说明(如果未完成)"}}
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": think_prompt}],
            temperature=0.1
        )
        
        content = response.choices[0].message.content
        
        # 提取 JSON
        import re
        json_match = re.search(r'\{.*\}', content, re.DOTALL)
        if json_match:
            try:
                return json.loads(json_match.group())
            except:
                pass
        
        return {"is_complete": True, "final_answer": "处理完成", "next_action": ""}
    
    def run(self, task: str) -> str:
        """主入口:执行多步任务"""
        
        print(f"\n{'='*60}")
        print(f"📋 新任务: {task}")
        print(f"{'='*60}")
        
        # 1. 重置状态
        self.reset()
        self.state["task"] = task
        
        # 2. 规划步骤
        print("\n📝 步骤1: 规划任务...")
        steps = self.plan(task)
        print(f"   规划出 {len(steps)} 个步骤:")
        for s in steps:
            print(f"   - 步骤{s.get('step_id')}: {s.get('description')}")
        
        self.state["pending_steps"] = steps.copy()
        
        # 3. 执行步骤(循环)
        print("\n🔧 步骤2: 执行步骤...")
        
        for step in steps:
            step_id = step.get("step_id")
            description = step.get("description", "")
            tool = step.get("tool", "")
            
            print(f"\n   📍 执行步骤 {step_id}: {description}")
            print(f"      工具: {tool}")
            
            # 执行
            result = self.execute_step(step)
            
            if result:
                print(f"      结果: {result[:100]}...")
                # 保存中间结果
                self.state["intermediate_results"][f"step_{step_id}"] = result
                self.state["completed_steps"].append({
                    "step_id": step_id,
                    "description": description,
                    "result": result
                })
            else:
                print(f"      无需工具,直接回答")
        
        # 4. 思考总结
        print("\n💭 步骤3: 思考总结...")
        conclusion = self.think_next(
            task,
            self.state["completed_steps"],
            self.state["intermediate_results"]
        )
        
        if conclusion.get("is_complete"):
            final_answer = conclusion.get("final_answer", "任务完成")
        else:
            # 如果未完成,让 AI 基于中间结果生成回答
            final_answer = self.generate_final_answer(task, self.state["intermediate_results"])
        
        self.state["final_answer"] = final_answer
        
        return final_answer
    
    def generate_final_answer(self, task: str, results: dict) -> str:
        """基于中间结果生成最终回答"""
        
        prompt = f"""
原始任务:{task}

收集到的信息:
{json.dumps(results, ensure_ascii=False, indent=2)}

请根据以上信息,给出一个完整、友好的回答。
"""
        
        response = self.client.chat.completions.create(
            model="deepseek-chat",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.5
        )
        
        return response.choices[0].message.content


# ========== 测试 ==========
if __name__ == "__main__":
    agent = StatefulAgent()
    
    # 测试用例
    tests = [
        "帮我规划一下去新疆北疆自驾游,大概10天左右,需要知道自驾路线、天气、景点以及每天的住宿信息。"
    ]
    
    for test in tests:
        result = agent.run(test)
        print(f"\n{'='*60}")
        print(f"✅ 最终回答: {result}")
        print(f"{'='*60}\n")

4.总结

概念 理解
状态管理 Agent 需要记住做了什么、还需要做什么
任务规划 复杂任务拆解成多个小步骤
循环执行 按步骤执行,保存中间结果
最终整合 用中间结果生成完整回答

这种设计使得 Agent 能够清晰地跟踪执行过程,为后续的思考和决策提供完整的上下文信息,是 ReAct 模式的重要组成部分,这个是有区别于一次回答的,关键写好提示词,返回好数据,然后根据回答调用工具函数,记录下结果以及当前到哪一步了,再次进行思考,进行下一步,最后总结,差不多就这样了。

相关推荐
YangYang9YangYan2 小时前
2026经管专业就业后学习数据分析的价值分析
学习·数据挖掘·数据分析
无限大.2 小时前
数字生存05:在AI时代,如何保持学习能力,不断成长
人工智能·学习
龙文浩_2 小时前
# AI的NLP学习/embedding/tensorboard
人工智能·学习·自然语言处理
Amazing_Cacao2 小时前
体系总览|学习路径更清晰(精品可可,精品巧克力)
学习
AI_零食2 小时前
Flutter 框架跨平台鸿蒙开发 - 颜色听觉化应用
学习·flutter·信息可视化·开源·harmonyos
一定要AK2 小时前
CSS 入门到精通全章节学习笔记(含 CSS3 核心特性)
css·笔记·学习
red_redemption2 小时前
自由学习记录(157)
学习·pyenv管理python
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(三):Linux线程 VS 进程,线程控制
java·linux·运维·服务器·c++·学习·ubuntu
徒 花2 小时前
Python知识学习07
windows·python·学习