Agent 核心机制解析:基于 ReAct 模式的智能旅行助手实战
一、概述
本文以chapter1_agent_demo.py为实战案例,深入剖析 Agent 的核心工作机制。这是一个智能旅行助手的简化实现,通过模拟工具 + 真实 LLM 调用,展示了一个完整 Agent 的 Thought-Action-Observation 循环。
学习目标
- 理解 Agent 的ReAct工作流程
- 掌握LLM 配置与上下文累积的原理
- 区分系统提示词与用户提示词的作用
- 熟悉外部工具的引入与调用流程
- 理解循环执行的意义与实现
二、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 配置
fromopenaiimportOpenAIclassOpenAICompatibleClient: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)defgenerate(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)returnresponse.choices[0].message.content3.3 关键配置参数
| 参数 | 作用 | 示例值 |
|---|---|---|
model | 指定使用的模型 | "deepseek-chat" |
api_key | API 认证密钥 | "sk-xxxxx" |
base_url | API 服务地址 | "" |
3.4 调用方式
# 实例化客户端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 的结构
prompt_history=[f"用户请求:{user_prompt}"]# 第1项:初始请求# 循环中不断追加foriinrange(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 上下文累积的代码实现
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 代码中的实现
messages=[{'role':'system','content':system_prompt},# 固定角色{'role':'user','content':prompt}# 变化的问题]5.3 系统提示词的设计
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 用户提示词的作用
用户提示词是驱动整个流程的动力:
user_prompt="你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"它决定了:
- LLM 第一次思考的方向
- 需要调用哪些工具
- 何时可以结束任务
六、LLM 输出的处理
6.1 为什么需要处理 LLM 输出?
LLM 返回的是自然语言文本,但程序需要:
- 知道下一步要执行什么动作
- 提取工具名和参数
- 判断任务是否完成
6.2 正则表达式解析
# 1. 提取 Action 部分action_match=re.search(r"Action: (.*)",llm_output,re.DOTALL)# re.DOTALL 让 . 能匹配换行符# 2. 判断是 Finish 还是工具调用ifaction_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 工具的定义方式
# 方式1:模拟工具(写死数据,方便测试)defget_weather(city:str)->str:weather_data={"北京":"晴天,26摄氏度","上海":"小雨,22摄氏度",# ...}returnf"{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 工具调用的代码实现
iftool_nameinavailable_tools:observation=available_tools[tool_name](**kwargs)# 解包参数调用else:observation=f"错误: 未定义的工具 '{tool_name}'"7.5 工具返回值的处理
工具返回值会作为Observation追加到历史,供下一次 LLM 调用:
Observation:北京当前天气:晴天,26摄氏度LLM 会根据这个「观察结果」决定下一步行动。
八、循环执行机制
8.1 为什么需要循环?
因为复杂任务无法一步完成:
用户:我想去北京旅游 │ ├── 步骤1:查天气 ──→ 晴天 │ ├── 步骤2:推荐景点 ──→ 故宫、长城 │ └── 步骤3:给出建议 ──→ 完成8.2 循环控制
foriinrange(5):# 最多执行5次,防止无限循环# ... 思考、行动、观察 ...ifaction_str.startswith("Finish"):break# 任务完成,主动退出8.3 循环终止条件
| 条件 | 说明 |
|---|---|
Action: Finish[答案] | 正常完成,LLM 认为任务已达成 |
| 循环次数达到上限 | i >= 5,防止死循环 |
| Action 解析失败 | 加入错误观察,继续尝试 |
8.4 循环的作用
- 多次推理:复杂问题分解为多个简单步骤
- 自我修正:如果某步错误,后续步骤可以纠正
- 信息累积:每次循环获取新信息,逐步接近答案
九、完整执行流程
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 设计原则
- 职责分离:Thought 给人看,Action 给机器执行
- 信息透明:完整的 prompt_history 让 LLM 拥有记忆
- 反馈闭环:每次工具调用都生成 Observation 供 LLM 参考
- 安全防护:循环次数限制防止无限执行
十一、附录
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")十一、总结
- 初始化:用户原始提问进入 prompt_history,作为初始用户侧唯一内容。
2. 拼接上下文:系统提示词 + 全量历史(从用户提问起的所有 Thought/Action/Observation)拼接成 full_prompt,传给 LLM。
3. LLM 输出:LLM 输出 llm_output,包含 Thought(推理过程)+ Action(执行指令)。
4. 程序解析 Action:
若 Action 是 Finish[答案]:
程序通过正则表达式从 Action 字符串中截取方括号内的内容作为最终答案。
直接输出该答案给用户,循环终止。
全程无工具调用,也不产生任何 Observation。
若 Action 是普通工具调用指令:
先把本轮 Thought+Action 追加进 prompt_history。
再进入 execute_action 执行工具,拿到 tool_result。
把 tool_result 包装成 Observation,追加进 prompt_history。
循环迭代:回到第2步,带着更新后的全量历史重新调用 LLM。
终止条件:命中 Finish 或 达到最大循环次数上限(如5次,防死循环)。