mirror of
https://github.com/jihe520/MathModelAgent.git
synced 2026-09-19 08:09:48 +08:00
72e0177745
rename
309 lines
12 KiB
Python
309 lines
12 KiB
Python
import json
|
|
from core.LLM import BaseModel
|
|
from models.user_output import UserOutput
|
|
from tools.code_interpreter import CodeInterpreter
|
|
from utils.common_utils import get_current_files
|
|
from utils.enums import CompTemplate, FormatOutPut
|
|
from utils.logger import log
|
|
from utils.RichPrinter import RichPrinter
|
|
from utils.notebook_serializer import NotebookSerializer
|
|
from .functions import functions
|
|
from .prompt import *
|
|
|
|
class Agent:
|
|
def __init__(
|
|
self,
|
|
model: BaseModel,
|
|
max_chat_turns: int = 30, # 单个agent最大对话轮次
|
|
user_output: UserOutput = None,
|
|
) -> None:
|
|
self.model = model
|
|
self.chat_history: list[dict] = [] # 存储对话历史
|
|
self.max_chat_turns = max_chat_turns # 最大对话轮次
|
|
self.current_chat_turns = 0 # 当前对话轮次计数器
|
|
self.user_output = user_output
|
|
|
|
def run(self, prompt: str, system_prompt: str) -> str:
|
|
"""
|
|
执行agent的对话并返回结果和总结
|
|
|
|
Args:
|
|
prompt: 输入的提示
|
|
|
|
Returns:
|
|
str: 模型的响应
|
|
"""
|
|
try:
|
|
RichPrinter.agent_start(self.__class__.__name__)
|
|
self.current_chat_turns = 0 # 重置对话轮次计数器
|
|
|
|
# 更新对话历史
|
|
self.append_chat_history({"role": "system", "content": system_prompt})
|
|
self.append_chat_history({"role": "user", "content": prompt})
|
|
|
|
# 获取历史消息用于本次对话
|
|
response = self.model.chat(
|
|
history=self.chat_history, agent_name=self.__class__.__name__
|
|
)
|
|
response_content = response.choices[0].message.content
|
|
self.chat_history.append({"role": "assistant", "content": response_content})
|
|
|
|
RichPrinter.agent_end(self.__class__.__name__)
|
|
return response_content
|
|
except Exception as e:
|
|
error_msg = f"执行过程中遇到错误: {str(e)}"
|
|
log.error(f"Agent执行失败: {str(e)}")
|
|
return error_msg
|
|
|
|
def append_chat_history(self, msg: dict) -> None:
|
|
self.chat_history.append(msg)
|
|
self.user_output.data_recorder.append_chat_history(
|
|
msg, agent_name=self.__class__.__name__
|
|
)
|
|
|
|
|
|
class ModelerAgent(Agent): # 继承自Agent类而不是BaseModel
|
|
def __init__(
|
|
self,
|
|
model: BaseModel,
|
|
max_chat_turns: int = 30, # 添加最大对话轮次限制
|
|
) -> None:
|
|
super().__init__(model, max_chat_turns)
|
|
self.system_prompt =MODELER_PROMPT
|
|
|
|
# 代码强
|
|
class CoderAgent(Agent): # 同样继承自Agent类
|
|
def __init__(
|
|
self,
|
|
model: BaseModel,
|
|
work_dir: str, # 工作目录
|
|
max_chat_turns: int = 60, # TODO: 配置文件
|
|
max_retries: int = 5, # 重试次数
|
|
user_output: UserOutput = None,
|
|
) -> None:
|
|
super().__init__(model, max_chat_turns, user_output)
|
|
self.work_dir = work_dir
|
|
self.notebook_serializer = NotebookSerializer(work_dir)
|
|
self.code_interpreter = CodeInterpreter(work_dir, self.notebook_serializer)
|
|
|
|
self.max_retries = max_retries
|
|
self.is_first_run = True
|
|
self.created_images: list[str] = [] # 当前任务创建的图片列表
|
|
self.system_prompt = CODER_PROMPT
|
|
|
|
self.created_images: list[str] = [] # 当前求解创建的图片
|
|
|
|
def run(self, prompt: str, subtask_title: str) -> str:
|
|
RichPrinter.agent_start(self.__class__.__name__)
|
|
self.created_images.clear() # 清空上一次任务的图片列表
|
|
# TODO: jupyter 的notebook 分节
|
|
self.notebook_serializer.add_markdown_segmentation_to_notebook(
|
|
content=prompt, segmentation=subtask_title
|
|
)
|
|
|
|
# 如果是第一次运行,则添加系统提示
|
|
if self.is_first_run:
|
|
self.is_first_run = False
|
|
self.append_chat_history({"role": "system", "content": self.system_prompt})
|
|
|
|
self.append_chat_history({"role": "user", "content": prompt})
|
|
|
|
retry_count = 0
|
|
last_error_message = ""
|
|
task_completed = False
|
|
|
|
if self.current_chat_turns >= self.max_chat_turns:
|
|
raise Exception(
|
|
f"Reached maximum number of chat turns ({self.max_chat_turns}). Task incomplete."
|
|
)
|
|
|
|
if retry_count >= self.max_retries:
|
|
raise Exception(
|
|
f"Failed to complete task after {self.max_retries} attempts. Last error: {last_error_message}"
|
|
)
|
|
|
|
try:
|
|
while (
|
|
not task_completed
|
|
and retry_count < self.max_retries
|
|
and self.current_chat_turns < self.max_chat_turns
|
|
):
|
|
self.current_chat_turns += 1
|
|
response = self.model.chat(
|
|
history=self.chat_history,
|
|
tools=functions,
|
|
tool_choice="auto",
|
|
agent_name=self.__class__.__name__,
|
|
)
|
|
|
|
if (
|
|
hasattr(response.choices[0].message, "tool_calls")
|
|
and response.choices[0].message.tool_calls
|
|
):
|
|
tool_call = response.choices[0].message.tool_calls[0]
|
|
tool_id = tool_call.id
|
|
# TODO: json JSON解析时遇到了无效的转义字符
|
|
if tool_call.function.name == "execute_code":
|
|
code = json.loads(tool_call.function.arguments)["code"]
|
|
full_content = response.choices[0].message.content
|
|
|
|
# 更新对话历史 - 添加助手的响应
|
|
self.append_chat_history(
|
|
{
|
|
"role": "assistant",
|
|
"content": full_content,
|
|
"tool_calls": [
|
|
{
|
|
"id": tool_id,
|
|
"type": "function",
|
|
"function": {
|
|
"name": "execute_code",
|
|
"arguments": json.dumps({"code": code}),
|
|
},
|
|
}
|
|
],
|
|
}
|
|
)
|
|
|
|
# 执行代码并获取结果
|
|
(
|
|
text_to_gpt,
|
|
content_to_display,
|
|
error_occurred,
|
|
error_message,
|
|
) = self.code_interpreter.jupyter_kernel.execute_code(code)
|
|
|
|
# 添加工具执行结果
|
|
self.append_chat_history(
|
|
{
|
|
"role": "tool",
|
|
"content": text_to_gpt,
|
|
"tool_call_id": tool_id,
|
|
}
|
|
)
|
|
|
|
if error_occurred:
|
|
retry_count += 1
|
|
last_error_message = error_message
|
|
reflection_prompt = get_reflection_prompt(error_message,code)
|
|
|
|
self.append_chat_history(
|
|
{"role": "user", "content": reflection_prompt}
|
|
)
|
|
continue
|
|
|
|
# 检查任务完成情况时也计入对话轮次
|
|
self.current_chat_turns += 1
|
|
completion_check_prompt = get_completion_check_prompt(prompt, text_to_gpt)
|
|
self.append_chat_history(
|
|
{"role": "user", "content": completion_check_prompt}
|
|
)
|
|
|
|
completion_response = self.model.chat(
|
|
history=self.chat_history,
|
|
tools=functions,
|
|
tool_choice="auto",
|
|
agent_name=self.__class__.__name__,
|
|
)
|
|
|
|
# # TODO: 压缩对话历史
|
|
# if completion_response.usage.prompt_tokens > self.max_tokens * 0.75:
|
|
# # 保留系统消息
|
|
# system_messages = [msg for msg in self.chat_history if msg["role"] == "system"]
|
|
# recent_messages = self.chat_history[-5:]
|
|
# self.chat_history = system_messages + recent_messages
|
|
# elif completion_response.usage.prompt_tokens > self.max_tokens:
|
|
# raise Exception("token超出限制")
|
|
|
|
if not (
|
|
hasattr(
|
|
completion_response.choices[0].message, "tool_calls"
|
|
)
|
|
and completion_response.choices[0].message.tool_calls
|
|
):
|
|
task_completed = True
|
|
RichPrinter.agent_end(self.__class__.__name__)
|
|
return completion_response.choices[0].message.content
|
|
if retry_count >= self.max_retries:
|
|
return f"Failed to complete task after {self.max_retries} attempts. Last error: {last_error_message}"
|
|
|
|
if self.current_chat_turns >= self.max_chat_turns:
|
|
return f"Reached maximum number of chat turns ({self.max_chat_turns}). Task incomplete."
|
|
|
|
RichPrinter.agent_end(self.__class__.__name__)
|
|
self._update_created_images()
|
|
return response.choices[0].message.content
|
|
except Exception as e:
|
|
error_msg = f"执行过程中遇到错误: {str(e)}"
|
|
log.error(f"Agent执行失败: {str(e)}")
|
|
return error_msg
|
|
|
|
def get_created_images(self) -> list[str]:
|
|
"""获取当前任务创建的图片列表"""
|
|
return self.created_images
|
|
|
|
def _update_created_images(self) -> None:
|
|
"""更新创建的图片列表"""
|
|
current_images = set(get_current_files(self.work_dir, "image"))
|
|
previous_images = set(self.created_images)
|
|
self.created_images = list(current_images - previous_images)
|
|
|
|
def get_notebook_serializer(self) -> NotebookSerializer:
|
|
return self.notebook_serializer
|
|
|
|
|
|
# 长文本
|
|
# TODO: 并行 parallel
|
|
class WriterAgent(Agent): # 同样继承自Agent类
|
|
def __init__(
|
|
self,
|
|
model: BaseModel,
|
|
max_chat_turns: int = 10, # 添加最大对话轮次限制
|
|
comp_template: CompTemplate = CompTemplate,
|
|
format_output: FormatOutPut = FormatOutPut.Markdown,
|
|
user_output: UserOutput = None,
|
|
) -> None:
|
|
super().__init__(model, max_chat_turns, user_output)
|
|
self.format_out_put = format_output
|
|
self.comp_template = comp_template
|
|
self.system_prompt = get_writer_prompt(format_output)
|
|
self.available_images: list[str] = []
|
|
|
|
def run(self, prompt: str, available_images: list[str] = None) -> str:
|
|
"""
|
|
执行写作任务
|
|
Args:
|
|
prompt: 写作提示
|
|
available_images: 可用的图片列表
|
|
"""
|
|
if available_images:
|
|
self.available_images = available_images
|
|
image_list = "\n".join([f"- {img}" for img in available_images])
|
|
image_prompt = (
|
|
f"\n可用的图片列表:\n{image_list}\n请在写作时适当引用这些图片。"
|
|
)
|
|
prompt = prompt + image_prompt
|
|
|
|
return super().run(prompt, self.system_prompt)
|
|
|
|
def summarize(self) -> str:
|
|
"""
|
|
总结对话内容
|
|
"""
|
|
try:
|
|
self.append_chat_history(
|
|
{"role": "user", "content": "请简单总结以上完成什么任务取得什么结果:"}
|
|
)
|
|
# 获取历史消息用于本次对话
|
|
response = self.model.chat(
|
|
history=self.chat_history, agent_name=self.__class__.__name__
|
|
)
|
|
self.append_chat_history(
|
|
{"role": "assistant", "content": response.choices[0].message.content}
|
|
)
|
|
return response.choices[0].message.content
|
|
except Exception as e:
|
|
log.error(f"总结生成失败: {str(e)}")
|
|
# 返回一个基础总结,避免完全失败
|
|
return "由于网络原因无法生成详细总结,但已完成主要任务处理。"
|