1. ReAct(推理-行动-观察)

图片

LLaMA 3在多步推理中的流程大致可分为以下几个步骤:

  • 推理(Thought):模型在执行每个行动时,基于已有知识和当前环境信息进行推理,以决定下一步的行动。
  • 工具调用(Act):模型根据推理结果调用外部工具,并执行相关操作。
  • 观察(Obs):模型在行动后观察结果,并分析这些结果与预期之间的差异。

1.1 代码

import ollama
import re
import logging
from sympy import sympify

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 创建指向本地服务器的客户端
client = ollama.Client(host='http://192.168.100.135:11434')

# Tool implementation
def lookup_fact(keyword):
"""
根据关键词检索预定义的事实。
"""
logger.info(f"正在查找事实: {keyword}")
facts = {
"光速": "光速约为299,792,458米每秒。",
"普朗克常数": "普朗克常数约为6.62607015 × 10^-34 Js。",
"地球质量": "地球的质量约为5.972 × 10^24千克。",
"月球质量": "月球的质量约为7.342 × 10^22千克。",
"speed of light": "光速约为299,792,458米每秒。",
"planck's constant": "普朗克常数约为6.62607015 × 10^-34 Js。",
"mass of earth": "地球的质量约为5.972 × 10^24千克。",
"mass of moon": "月球的质量约为7.342 × 10^22千克。"
}
result = facts.get(keyword.lower(), "未找到相关信息")
logger.info(f"查找结果: {result}")
return result

def calculate(expression):
"""
安全地计算数学表达式。
"""
logger.info(f"正在计算: {expression}")
try:
result = float(sympify(expression))
logger.info(f"计算结果: {result}")
return result
except Exception as e:
logger.error(f"计算错误: {e}")
return f"计算错误: {e}"

# Tool registry
class ToolRegistry:
def __init__(self):
self.tools = {}

def register(self, name, description, handler):
logger.debug(f"Registering tool: {name}, Description: {description}")
self.tools[name] = {"description": description, "handler": handler}

def get_handler(self, name):
handler = self.tools.get(name, {}).get("handler")
if handler:
logger.debug(f"Found handler for tool: {name}")
else:
logger.warning(f"Handler for tool not found: {name}")
return handler

# Initialize tool registry
tool_registry = ToolRegistry()
tool_registry.register("lookup_fact", "根据关键词检索预定义事实(有效键值:光速、普朗克常数、地球质量、月球质量)", lookup_fact)
tool_registry.register("calculate", "执行数学表达式计算", calculate)

# Generate prompt
def generate_prompt(tools):
tool_descriptions = "\n".join(
[f"{i+1}. {name}: 功能: {desc['description']} 使用格式: {name}: <参数>"
for i, (name, desc) in enumerate(tools.items())]
)

return (
"你将扮演一个ReAct代理,逐步解决用户的问题,严格按照以下步骤执行:\n"
"工作流程:\n"
"1. 思考: 描述你对给定问题的思考过程。\n"
"2. 行动: 选择一个可用工具并执行操作。每次只执行一个操作,等待观察结果后再继续。\n"
"3. 观察: 记录工具返回的结果。\n"
"4. 答案: 问题解决后输出最终答案。\n"
"可用工具:\n"
f"{tool_descriptions}\n"
"对话示例:\n"
"问题: 查找地球和月球的质量,并计算它们的质量比。\n"
"思考: 我需要查找地球和月球的质量。\n"
"行动: lookup_fact 地球质量\n"
"观察: 地球的质量约为5.972 × 10^24千克。\n"
"思考: 接下来,我需要查找月球的质量。\n"
"行动: lookup_fact 月球质量\n"
"观察: 月球的质量约为7.342 × 10^22千克。\n"
"思考: 现在我已经得到了地球和月球的质量,可以计算它们的质量比。\n"
"行动: calculate 5.972e24 / 7.342e22\n"
"观察: 结果是81.35215091343292。\n"
"答案: 地球的质量大约是月球质量的81.35倍。\n"
"\n重要: 每次响应只能提供单行内容(思考或行动或观察)。如果没有需要执行的操作,则继续推理。"
).strip()

# Prompt
prompt = generate_prompt(tool_registry.tools)

# Regular expression for extracting action commands
action_re = re.compile(r'^Action:\s*(\w+)\s+(.*)$', re.IGNORECASE | re.MULTILINE)

# ChatBot implementation
class ChatBot:
def __init__(self, system=""):
self.messages = [{"role": "system", "content": system}] if system else []

def __call__(self, message):
self.messages.append({"role": "user", "content": message})
try:
logger.debug(f"发送消息到模型: {message}")
response = client.chat(model="llama3.2:1b", messages=self.messages)
content = response['message']['content'].splitlines()[0] # 确保只返回一行响应
logger.debug(f"模型响应: {content}")
self.messages.append({"role": "assistant", "content": content})
return content
except Exception as e:
logger.error(f"API调用失败: {e}")
return "发生错误,无法继续对话。"

def trim_messages(self, max_messages=10):
logger.debug(f"修剪消息历史以保留最后 {max_messages} 条消息")
self.messages = self.messages[-max_messages:]

# Main query function
def query(question, max_turns=5):
bot = ChatBot(prompt)
next_prompt = question
for turn in range(max_turns):
logger.info(f"---- 第 {turn + 1} 轮 ----")
logger.debug(f"当前提示: {next_prompt}")
result = bot(next_prompt)
logger.info(f"AI响应: {result}")

# 提取行动命令
actions = [action_re.match(line) for line in result.splitlines() if action_re.match(line)]
logger.debug(f"找到的行动匹配: {actions}")
if actions:
# 解析第一个有效的行动命令
action, action_input = actions[0].groups()
logger.debug(f"提取的行动: {action}, 参数: {action_input}")
handler = tool_registry.get_handler(action)
if handler:
logger.info(f" -- 执行行动: {action} 参数: {action_input}")
observation = handler(action_input)

# 将观察结果整合到上下文中
observation_message = f"观察: {observation}"
logger.info(f"观察结果: {observation_message}")

# 根据观察结果更新下一个提示以继续解决问题
next_prompt = f"{result}\n{observation_message}\n思考:"
bot.trim_messages()

# 将观察结果添加到消息历史中以保持对话连贯
bot.messages.append({"role": "assistant", "content": observation_message})
else:
logger.error(f"未知行动: {action}")
break
else:
# 如果没有找到行动,则继续推理
next_prompt = f"{result}\n思考:"
bot.trim_messages()

# Example execution
if __name__ == "__main__":
question = "查找光速和普朗克常数的值,并计算它们的乘积。"
query(question)

1.2 输出

INFO:__main__:---- Turn 1 ----
INFO:httpx:HTTP Request: POST http://192.168.100.135:11434/api/chat "HTTP/1.1 200 OK"
INFO:__main__:AI response: 思考:我需要查找光速(c)和普朗克常数(k_B)的值。
INFO:__main__:---- Turn 2 ----
INFO:httpx:HTTP Request: POST http://192.168.100.135:11434/api/chat "HTTP/1.1 200 OK"
INFO:__main__:AI response: lookup_fact speed of light c: 299792458 m/s普朗克常数 k_B: 1.380649e-23 J/K
INFO:__main__:---- Turn 3 ----
INFO:httpx:HTTP Request: POST http://192.168.100.135:11434/api/chat "HTTP/1.1 200 OK"
INFO:__main__:AI response: 观察: 光速(c)等于3×108本秒,普朗克常数(k_B)等于每摄氏度的热量为1.380648e-23 joule。
INFO:__main__:---- Turn 4 ----
INFO:httpx:HTTP Request: POST http://192.168.100.135:11434/api/chat "HTTP/1.1 200 OK"
INFO:__main__:AI response: 行动: calculate c * k_B
INFO:__main__:---- Turn 5 ----
INFO:httpx:HTTP Request: POST http://192.168.100.135:11434/api/chat "HTTP/1.1 200 OK"
INFO:__main__:AI response: 计算结果:3×108本秒 * 1.380648e-23 J/K = 4.18579628 × 10^(-21) J

Process finished with exit code 0

本站由 卡卡龙 使用 Stellar 1.33.1主题创建

本站访问量 次. 本文阅读量 次.