Agent 核心机制解析:基于 ReAct 模式的智能旅行助手实战

Agent 核心机制解析:基于 ReAct 模式的智能旅行助手实战

一、概述

本文以 chapter1_agent_demo.py 为实战案例,深入剖析 Agent 的核心工作机制。这是一个智能旅行助手的简化实现,通过模拟工具 + 真实 LLM 调用,展示了一个完整 Agent 的 Thought-Action-Observation 循环。

学习目标

  1. 理解 Agent 的 ReAct 工作流程
  2. 掌握 LLM 配置上下文累积的原理
  3. 区分系统提示词用户提示词的作用
  4. 熟悉外部工具的引入与调用流程
  5. 理解循环执行的意义与实现

二、ReAct 工作流程详解

2.1 什么是 ReAct?

ReAct = Reasoning + Acting

这是一种让大模型能够「思考」并「行动」的模式,核心思想是:

复制代码
用户请求 → 思考(Reason) → 行动(Action) → 观察(Observation) → 思考(Reason) → ...

不断循环,直到任务完成。

2.2 完整工作流程图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                      ReAct 循环流程                           │
└─────────────────────────────────────────────────────────────┘

     ┌──────────────────┐
     │   用户请求输入     │
     └────────┬─────────┘
              ▼
     ┌──────────────────┐
     │  构建完整上下文    │  ← prompt_history 累积历史
     │  (系统提示词+      │
     │   用户请求+        │
     │   历史对话)        │
     └────────┬─────────┘
                       ▼
     ┌──────────────────┐
     │   调用 LLM       │  ← 获取 Thought + Action
     │  OpenAI API     │
     └────────┬─────────┘
              ▼
     ┌──────────────────┐
     │   解析 Action    │
     │  正则提取工具名   │
     │  和参数          │
     └────────┬─────────┘
              ▼
     ┌──────────────────┐
     │ 判断: 工具调用    │────Yes────► 调用工具 ──► 获取 Observation
     │   还是结束?       │                  │
     └────────┬─────────┘                  │
                         ▼                             ▼
     ┌──────────────────┐         ┌──────────────────┐
     │   输出最终答案    │◄────────│ 返回循环继续思考   │
     │   Finish[答案]   │         └──────────────────┘
     └──────────────────┘    
     ```
 ### 2.3 代码中的循环实现

```python
for i in range(5):  # 最多循环5次,防止无限循环
    # 1. 构建上下文
    full_prompt = "\n".join(prompt_history)
    
    # 2. 调用 LLM
    llm_output = llm_client.generate(full_prompt, system_prompt=AGENT_SYSTEM_PROMPT)
    
    # 3. 解析 Action
    action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
    
    # 4. 判断结束还是继续
    if action_str.startswith("Finish"):
        break  # 任务完成,退出循环
    else:
        # 调用工具,获取 Observation,继续循环

三、LLM 配置

3.1 为什么要 LLM 配置?

LLM 是 Agent 的「大脑」,负责:

  • 理解用户意图
  • 制定执行计划
  • 决定下一步行动
  • 生成最终回答

3.2 代码中的 LLM 配置

python 复制代码
from openai import OpenAI

class OpenAICompatibleClient:
    def __init__(self, model: str, api_key: str, base_url: str):
        self.model = model              # 模型标识
        self.client = OpenAI(            # API 客户端
            api_key=api_key, 
            base_url=base_url
        )
    
    def generate(self, prompt: str, system_prompt: str) -> str:
       messages = [
            {'role': 'system', 'content': system_prompt},  # 系统提示词
            {'role': 'user', 'content': prompt}           # 用户输入
        ]
        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            stream=False
        )
        return response.choices[0].message.content

3.3 关键配置参数

参数 作用 示例值
model 指定使用的模型 "deepseek-chat"
api_key API 认证密钥 "sk-xxxxx"
base_url API 服务地址 ""

3.4 调用方式

python 复制代码
# 实例化客户端
llm = OpenAICompatibleClient(
    model="",
    api_key="",
    base_url=""
)

# 调用生成
llm_output = llm.generate(
    prompt="用户的问题", 
    system_prompt="系统提示词"
)

四、上下文累积机制

4.1 什么是上下文?

上下文 = 让 LLM 知道「之前发生了什么」

LLM 本身没有记忆 ,每次调用都是独立的。通过 prompt_history,我们为 LLM 构建了一个「会话记忆」。

4.2 prompt_history 的结构

python 复制代码
prompt_history = [f"用户请求: {user_prompt}"]  # 第1项:初始请求

# 循环中不断追加
for i in range(5):
    # ... LLM 调用 ...
    prompt_history.append(llm_output)  # 第2、4、6...项:LLM 的输出
    # ... 工具调用 ...
    prompt_history.append(f"Observation: {observation}")  # 第3、5、7...项:工具结果

4.3 完整的 prompt_history 示例

假设用户问「北京天气和景点」:

复制代码
[
    "用户请求: 你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。",
    
    "Thought: 用户想知道北京天气,然后根据天气推荐景点。我应该先查询天气。
     Action: get_weather(city=\"北京\")",
    
    "Observation: 北京当前天气:晴天,26摄氏度",
    "Thought: 北京是晴天,适合去故宫或长城。我需要调用景点推荐工具。
     Action: get_attraction(city=\"北京\", weather=\"晴天\")",
    
    "Observation: 根据您的情况,为您推荐:推荐故宫(明清皇宫,门票60元)和长城(世界奇迹,建议提前预约)",
    
    "Thought: 已经获取到天气和景点信息,可以给用户最终回答了。
     Action: Finish[北京今天天气晴朗,26摄氏度。建议您去故宫和长城游玩...]"
]

4.4 为什么同时追加 llm_output 和 observation?

追加内容 作用 目的
llm_output LLM 的 Thought + Action 让下次 LLM 看到自己的推理过程,实现自我修正
observation 工具执行结果 让 LLM 知道真实世界的状态

类比:就像人类解决问题时,会同时记住「我怎么想的」和「实际发生了什么」。

4.5 上下文累积的代码实现

python 复制代码
full_prompt = "\n".join(prompt_history)  # 拼接所有历史为单个字符串

llm_output = llm_client.generate(
    full_prompt,                    # 累积的上下文
    system_prompt=AGENT_SYSTEM_PROMPT  # 系统提示词
)

五、系统提示词与用户提示词

5.1 两者的本质区别

对比项 系统提示词 (System Prompt) 用户提示词 (User Prompt)
定义 给 LLM 的「角色说明」和「行为规范」 用户实际的「问题或请求」
位置 messages[0],固定不变 messages[1],每次变化
作用 告诉 LLM 「你是谁,该怎么做」 告诉 LLM 「要做什么」
变化频率 通常固定 每次对话都可能不同

5.2 代码中的实现

python 复制代码
messages = [
    {'role': 'system', 'content': system_prompt},  # 固定角色
    {'role': 'user', 'content': prompt}            # 变化的问题
]

5.3 系统提示词的设计

python 复制代码
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。

# 可用工具:
- `get_weather(city: str)`: 查询指定城市的天气。
- `get_attraction(city: str, weather: str)`: 根据城市和天气推荐旅游景点。

# 输出格式要求:
你的每次回复必须严格遵循以下格式,包含一对Thought和Action:

Thought: [你的思考过程和下一步计划]
Action: [你要执行的具体行动]
Action的格式必须是以下之一:
1. 调用工具:function_name(arg_name="arg_value")
2. 结束任务:Finish[最终答案]

# 重要提示:
- 每次只输出一对Thought-Action
- Action必须在同一行,不要换行
- 当收集到足够信息可以回答用户问题时,必须使用 Action: Finish[最终答案] 格式结束
"""

5.4 系统提示词的组成

复制代码
┌─────────────────────────────────────────────────────────┐
│                    系统提示词结构                          │
├─────────────────────────────────────────────────────────┤
│  1. 角色定义                                             │
│     "你是一个智能旅行助手"                                 │
│                                                          │
│  2. 工具说明                                              │
│     "可用工具: get_weather, get_attraction"              │
│                                                          │
│  3. 输出格式规范                                          │
│     "Thought: ... \n Action: ..." 
│ 4. 约束条件                                             │
│     "每次只输出一对Thought-Action"                        │
│     "Action必须在同一行"                                   │
│     "使用Finish[答案]结束"                          
└─────────────────────────────────────────────────────────┘

5.5 用户提示词的作用

用户提示词是驱动整个流程的动力

python 复制代码
user_prompt = "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"

它决定了:

  1. LLM 第一次思考的方向
  2. 需要调用哪些工具
  3. 何时可以结束任务

六、LLM 输出的处理

6.1 为什么需要处理 LLM 输出?

LLM 返回的是自然语言文本,但程序需要:

  1. 知道下一步要执行什么动作
  2. 提取工具名参数
  3. 判断任务是否完成

6.2 正则表达式解析

python 复制代码
# 1. 提取 Action 部分
action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
# re.DOTALL 让 . 能匹配换行符

# 2. 判断是 Finish 还是工具调用
if action_str.startswith("Finish"):
    # 提取最终答案
    final_answer = re.match(r"Finish\[(.*)\]", action_str).group(1)
else:
    # 3. 提取工具名
    tool_name = re.search(r"(\w+)\(", action_str).group(1)
    #    例如: get_weather(city="北京") → 提取 "get_weather"
    
    # 4. 提取参数
    args_str = re.search(r"\((.*)\)", action_str).group
     #    例如: city="北京" → 提取 'city="北京"'
    
    kwargs = dict(re.findall(r'(\w+)="([^"]*)"', args_str))
    #    解析为字典: {"city": "北京"}

6.3 解析流程图

复制代码
LLM 输出:
┌────────────────────────────────────────────────────────┐
│ Thought: 我需要查询北京的天气                            │
│ Action: get_weather(city="北京")                        │
└────────────────────────────────────────────────────────┘
                          │
                          ▼
┌────────────────────────────────────────────────────────┐
│ 正则提取 Action                                          │
│  action_match.group(1) = "get_weather(city=\"北京\")"   │
└────────────────────────────────────────────────────────┘
                          │
                          ▼
┌────────────────────────────────────────────────────────┐
│ 工具名提取: tool_name = "get_weather"                   │
│ 参数提取: kwargs = {"city": "北京"}                      │
└────────────────────────────────────────────────────────┘

6.4 为什么只处理 Action,不处理 Thought?

部分 作用 处理方式
Thought 给人看的,理解 LLM 思考过程 只作为文本追加到历史
Action 给程序执行的,决定下一步行动 正则解析,程序化执行

设计思想:Thought 是透明化 AI 思考过程,Action 是实际执行的接口。

七、外部工具的引入与调用

7.1 为什么要引入外部工具?

LLM 的局限:

  • 不知道实时信息(天气、股票、新闻等)
  • 无法执行操作(查数据库、发邮件等)

工具的作用:为 LLM 补全信息 + 赋予行动能力

7.2 工具的定义方式

python 复制代码
# 方式1:模拟工具(写死数据,方便测试)
def get_weather(city: str) -> str:
    weather_data = {
        "北京": "晴天,26摄氏度",
        "上海": "小雨,22摄氏度",
        # ...
    }
    return f"{city}当前天气:{weather_data[city]}"

# 方式2:注册到工具字典
available_tools = {
    "get_weather": get_weather,
    "get_attraction": get_attraction,
}

7.3 工具调用的完整流程

复制代码
1. LLM 输出 "Action: get_weather(city="北京")"

2. 代码解析:
   tool_name = "get_weather"
   kwargs = {"city": "北京"}

3. 检查工具是否存在:
   if tool_name in available_tools:  # True

4. 调用工具:
   observation = available_tools[tool_name](**kwargs)
   # 即: get_weather(city="北京")

5. 获取结果:
   observation = "北京当前天气:晴天,26摄氏度"

6. 追加到历史:
   prompt_history.append(f"Observation: {observation}")

7.4 工具调用的代码实现

python 复制代码
if tool_name in available_tools:
    observation = available_tools[tool_name](**kwargs)  # 解包参数调用
else:
    observation = f"错误: 未定义的工具 '{tool_name}'"

7.5 工具返回值的处理

工具返回值会作为 Observation 追加到历史,供下一次 LLM 调用:

python 复制代码
Observation: 北京当前天气:晴天,26摄氏度

LLM 会根据这个「观察结果」决定下一步行动。


八、循环执行机制

8.1 为什么需要循环?

因为复杂任务无法一步完成

复制代码
用户:我想去北京旅游
     │
     ├── 步骤1:查天气 ──→ 晴天
     │
     ├── 步骤2:推荐景点 ──→ 故宫、长城
     │
     └── 步骤3:给出建议 ──→ 完成

8.2 循环控制

python 复制代码
for i in range(5):  # 最多执行5次,防止无限循环
    # ... 思考、行动、观察 ...
    
    if action_str.startswith("Finish"):
        break  # 任务完成,主动退出

8.3 循环终止条件

条件 说明
Action: Finish[答案] 正常完成,LLM 认为任务已达成
循环次数达到上限 i >= 5,防止死循环
Action 解析失败 加入错误观察,继续尝试

8.4 循环的作用

  1. 多次推理:复杂问题分解为多个简单步骤
  2. 自我修正:如果某步错误,后续步骤可以纠正
  3. 信息累积:每次循环获取新信息,逐步接近答案

九、完整执行流程

9.1 场景:查询北京天气和景点

复制代码
用户输入: "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"

9.2 第一轮循环

复制代码
┌─────────────────────────────────────────────────────────┐
│  循环 1                                                   │
├─────────────────────────────────────────────────────────┤
│  输入: 用户请求                                           │
│                                                          │
│  LLM 输出:                                               │
│    Thought: 用户想了解北京天气和景点推荐。我应该先查询天气。   │
│    Action: get_weather(city="北京")                      │
│                                                                   │
│  工具调用: get_weather(city="北京")                       │
│  工具返回: 北京当前天气:晴天,26摄氏度                      │
│                                                          │
│  Observation: 北京当前天气:晴天,26摄氏度                 │
└─────────────────────────────────────────────────────────┘

9.3 第二轮循环

复制代码
┌─────────────────────────────────────────────────────────┐
│  循环 2                                                   │
├─────────────────────────────────────────────────────────┤
│  输入: 用户请求 + LLM输出 + Observation                   │
│                                                          │
│  LLM 输出:                                               │
│    Thought: 北京是晴天,适合去故宫或长城。我需要调用景点推荐。│
│    Action: get_attraction(city="北京", weather="晴天")    │
│                工具调用: get_attraction(city="北京", weather="晴天")    │
│  工具返回: 根据您的情况,为您推荐:故宫和长城                │
│                                                          │
│  Observation: 根据您的情况,为您推荐:故宫和长城            │
└─────────────────────────────────────────────────────────┘

9.4 第三轮循环

复制代码
┌─────────────────────────────────────────────────────────┐
│  循环 3                                                   │
├─────────────────────────────────────────────────────────┤
│  输入: 完整历史                                           │
│                                                          │
│  LLM 输出:                                               │
│    Thought: 已获取天气和景点信息,可以给用户最终回答了。     │
│    Action: Finish[北京今天天气晴朗,26摄氏度。建议您去故宫   │
│            和长城游玩。故宫门票60元,长城建议提前预约。]      │
│                                                          │
│  ✓ 任务完成,退出循环                                     │
└─────────────────────────────────────────────────────────┘

十、核心设计思想总结

10.1 架构概览

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        用户请求                              │
└──────────────────────────┬──── │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    LLM (大脑)                                │
│  - 理解任务                                                  │
│  - 制定计划                                                  │
│  - 决策行动                                                  │
└──────────────────────────┬──────────────────────────────────┘──────────────────────────────┘        │
          ┌────────────────┼────────────────┐
          │                │                │
          ▼                ▼                ▼
    ┌──────────┐   ┌──────────────┐   ┌──────────┐
    │ 工具1    │   │  工具2        │   │  工具3   │
    │ get_weather│   │ get_attraction│   │   ...   │
    └──────────┘   └──────────────┘   └──────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
                   ┌─────────────────────────────────────────────────────────────┐
│                   Observation (反馈)                        │
│  "天气是晴天"  "推荐故宫和长城"                              │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
              ┌─────────────────────────┐
              │  继续思考 or 结束任务     │
              └─────────────────────────┘

10.2 关键知识点

知识点 核心要点
ReAct 模式 Thought-Action-Observation 循环
LLM 配置 model、api_key、base_url 三要素
上下文累积 prompt_history 保存完整对话历史
系统提示词 固定的角色定义和行为规范
用户提示词 变化的任务输入
输出解析 正则提取 Action,执行工具或结束任务
工具调用 字典注册 + 动态调用 + 结果反馈
循环执行 分解复杂任务,支持自我修正

10.3 设计原则

  1. 职责分离:Thought 给人看,Action 给机器执行
  2. 信息透明:完整的 prompt_history 让 LLM 拥有记忆
  3. 反馈闭环:每次工具调用都生成 Observation 供 LLM 参考
  4. 安全防护:循环次数限制防止无限执行

十一、附录

11.1 完整代码结构

复制代码
chapter1_agent_demo.py
├── AGENT_SYSTEM_PROMPT     # 系统提示词
├── get_weather()           # 天气工具(Mock)
├── get_attraction()        # 景点工具(Mock)
├── available_tools         # 工具注册表
├── OpenAICompatibleClient  # LLM 客户端
└── run_agent()            # 主循环

12.2 关键正则表达式

正则 作用 示例
r"Action: (.*)" 提取 Action 行 Action: get_weather(city="北京")
r"(\w+)\(" 提取工具名 get_weather
r"\((.*)\)" 提取参数部分 city="北京"
r'(\w+)="([^"]*)"' 解析参数 {"city": "北京"}
r"Finish\[(.*)\]" 提取最终答案 Finish[北京天气晴朗...]

chapter1_agent_demo.py

复制代码
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。

# 可用工具:
- `get_weather(city: str)`: 查询指定城市的天气。
- `get_attraction(city: str, weather: str)`: 根据城市和天气推荐旅游景点。

# 输出格式要求:
你的每次回复必须严格遵循以下格式,包含一对Thought和Action:

Thought: [你的思考过程和下一步计划]
Action: [你要执行的具体行动]

Action的格式必须是以下之一:
1. 调用工具:function_name(arg_name="arg_value")
2. 结束任务:Finish[最终答案]

# 重要提示:
- 每次只输出一对Thought-Action
- Action必须在同一行,不要换行
- 当收集到足够信息可以回答用户问题时,必须使用 Action: Finish[最终答案] 格式结束

请开始吧!
"""
def get_weather(city: str) -> str:
    """
    获取天气的模拟函数(写死数据,方便测试)
    """
    weather_data = {
        "北京": "晴天,26摄氏度",
        "上海": "小雨,22摄氏度",
        "广州": "多云,28摄氏度",
        "深圳": "晴天,30摄氏度",
        "成都": "阴天,20摄氏度",
        "杭州": "小雨,24摄氏度",
    }
    if city in weather_data:
        return f"{city}当前天气:{weather_data[city]}"
    return f"{city}当前天气:晴天,25摄氏度(默认)"
 def get_attraction(city: str, weather: str) -> str:
    """
    获取景点的模拟函数(写死数据,方便测试)
    """
    attractions = {
        "北京": {
            "晴天": "推荐故宫(明清皇宫,门票60元)和长城(世界奇迹,建议提前预约)",
            "小雨": "推荐国家博物馆(免费,周一闭馆)和798艺术区(室内文艺打卡地)",
            "阴天": "推荐颐和园(皇家园林,湖光山色)和天坛(古代祭天建筑)",
            "多云": "推荐故宫和颐和园",
            "默认": "推荐故宫和长城",
        },
        "上海": {
            "晴天": "推荐外滩(万国建筑博览)和东方明珠(浦东地标)",
            "小雨": "推荐上海博物馆(免费,周一闭馆)和田子坊(老上海风情)",
             "阴天": "推荐豫园(古典园林)和南京路步行街",
            "多云": "推荐外滩和豫园",
            "默认": "推荐外滩和东方明珠",
        },
        "广州": {
            "晴天": "推荐广州塔(小蛮腰,夜景超美)和白云山(羊城第一高峰)",
            "小雨": "推荐陈家祠(岭南建筑艺术)和北京路步行街(千年古道)",
            "阴天": "推荐沙面岛(欧陆风情建筑群)和珠江夜游",
            "多云": "推荐广州塔和沙面岛",
            "默认": "推荐广州塔和白云山",
        },
         "深圳": {
            "晴天": "推荐世界之窗(不出国门的环球旅行)和东部华侨城(生态旅游)",
            "小雨": "推荐深圳博物馆(免费)和华强北商业街(科技产品)",
            "阴天": "推荐欢乐谷(大型主题乐园)和东门老街(深圳商业起源地)",
            "多云": "推荐世界之窗和东部华侨城",
            "默认": "推荐世界之窗和东部华侨城",
        },
        "成都": {
            "晴天": "推荐大熊猫繁育研究基地(国宝家园)和宽窄巷子(老成都生活)",
            "小雨": "推荐武侯祠(三国文化)和锦里古街(夜景超美)",
            "阴天": "推荐杜甫草堂(诗圣故居)和春熙路(时尚购物)",
            "多云": "推荐大熊猫基地和宽窄巷子",
            "默认": "推荐大熊猫基地和宽窄巷子",
        },
        "杭州": {
            "晴天": "推荐西湖(人间天堂,十景各异)和灵隐寺(千年古刹)",
            "小雨": "推荐宋城(千古情演出)和河坊街(老杭州风情)",
            "阴天": "推荐西溪湿地(城市绿肺)和雷峰塔(西湖最佳观景点)",
            "多云": "推荐西湖和灵隐寺",
            "默认": "推荐西湖和灵隐寺",
        },
    }
    "杭州": {
            "晴天": "推荐西湖(人间天堂,十景各异)和灵隐寺(千年古刹)",
            "小雨": "推荐宋城(千古情演出)和河坊街(老杭州风情)",
            "阴天": "推荐西溪湿地(城市绿肺)和雷峰塔(西湖最佳观景点)",
            "多云": "推荐西湖和灵隐寺",
            "默认": "推荐西湖和灵隐寺",
        },
    }
      city_attractions = attractions.get(city, attractions["北京"])
    weather_key = weather if weather in city_attractions else "默认"
    return f"根据您的情况,为您推荐:{city_attractions.get(weather_key, city_attractions['默认'])}"


available_tools = {
    "get_weather": get_weather,
    "get_attraction": get_attraction,
}
from openai import OpenAI

class OpenAICompatibleClient:
    """
    调用真实大模型(需配置 API)
    """
    
    def __init__(self, model: str, api_key: str, base_url: str):
        self.model = model
        self.client = OpenAI(api_key=api_key, base_url=base_url)
    
    def generate(self, prompt: str, system_prompt: str) -> str:
        """调用大模型 API"""
        print("[OpenAICompatibleClient] 正在调用大模型...")
        try:
            messages = [
                {'role': 'system', 'content': system_prompt},
                {'role': 'user', 'content': prompt}
            ]
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                stream=False
            )
            answer = response.choices[0].message.content
            print("[OpenAICompatibleClient] 大模型响应成功")
            return answer
        except Exception as e:
           print(f"[OpenAICompatibleClient] 调用失败: {e}")
            return f"错误:调用语言模型服务时出错。"
import re

def run_agent(user_prompt: str, llm_client):
    """
    运行 Agent 的主循环
    """
    print("=" * 60)
    print("* 智能旅行助手 Agent(工具Mock + LLM真实调用)")
    print("=" * 60)
    print(f"\n> 用户请求: {user_prompt}\n")
    
    prompt_history = [f"用户请求: {user_prompt}"]
    for i in range(5):
        print(f"\n{'-'*60}")
        print(f"循环 {i+1}")
        print(f"{'-'*60}")
        
        full_prompt = "\n".join(prompt_history)
        llm_output = llm_client.generate(full_prompt, system_prompt=AGENT_SYSTEM_PROMPT)
        
        print(f"\n[LLM 输出]")
        print(llm_output)
        
        prompt_history.append(llm_output)
        
        action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
        if not action_match:
           observation = "错误: 未能解析到 Action 字段"
            print(f"\n[观察] {observation}")
            prompt_history.append(f"Observation: {observation}")
            continue
        
        action_str = action_match.group(1).strip()
        if action_str.startswith("Finish"):
            final_answer = re.match(r"Finish\[(.*)\]", action_str).group(1)
            print(f"\n*** 任务完成!")
            print(f"最终答案: {final_answer}")
            break
        
        tool_name = re.search(r"(\w+)\(", action_str).group(1)
        args_str = re.search(r"\((.*)\)", action_str).group(1)
        kwargs = dict(re.findall(r'(\w+)="([^"]*)"', args_str))
        
        print(f"\n[调用工具] {tool_name}")
        print(f"[参数] {kwargs}")
        if tool_name in available_tools:
            observation = available_tools[tool_name](**kwargs)
        else:
            observation = f"错误: 未定义的工具 '{tool_name}'"
        
        print(f"\n[工具返回] {observation}")
        prompt_history.append(f"Observation: {observation}")
         print("\n" + "=" * 60)
    print("对话历史:")
    print("=" * 60)
    for i, msg in enumerate(prompt_history, 1):
        print(f"\n[{i}] {msg}")
    
    return prompt_history
if __name__ == "__main__":
    print("\n" + "~" * 50)
    print("\n开始测试 Agent...\n")
    
    API_KEY = ""
    BASE_URL = ""
    MODEL_ID = "t"
    
    llm = OpenAICompatibleClient(
        model=MODEL_ID,
        api_key=API_KEY,
        base_url=BASE_URL
    )
    
    user_prompt = "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"
    run_agent(user_prompt, llm)
    
    print("\n" + "~" * 50)
    print("\n测试完成!\n")