mirror of
https://github.com/jihe520/MathModelAgent.git
synced 2026-10-19 05:40:21 +08:00
- 后端:新增 POST /modeling/{task_id}/cancel 取消接口
- 后端:实现 asyncio.Event 取消机制,支持中断 LLM 调用
- 前端:任务详情页添加"停止运行"按钮
- 修复:取消后避免调用 md_2_docx 的错误
220 lines
9.7 KiB
Python
220 lines
9.7 KiB
Python
"""代码手 Agent 模块,负责生成和执行 Python 代码完成建模任务。"""
|
|
|
|
import asyncio
|
|
from app.core.agents.agent import Agent
|
|
from app.config.setting import settings, ApiType
|
|
from app.utils.log_util import logger
|
|
from app.services.redis_manager import redis_manager
|
|
from app.schemas.response import SystemMessage, InterpreterMessage
|
|
from app.tools.base_interpreter import BaseCodeInterpreter
|
|
from app.core.llm.llm import LLM
|
|
from app.schemas.A2A import CoderToWriter
|
|
from app.core.prompts import CODER_PROMPT
|
|
from app.utils.common_utils import get_current_files
|
|
import json
|
|
from app.core.prompts import get_reflection_prompt
|
|
from app.core.functions import coder_tools, coder_tools_anthropic
|
|
|
|
# TODO: 时间等待过久,stop 进程
|
|
# TODO: 支持 cuda
|
|
# TODO: 引入创新方案:
|
|
|
|
|
|
class CoderAgent(Agent):
|
|
"""代码手 Agent,通过 LLM 生成代码并在解释器中执行,支持错误反思和重试。"""
|
|
def __init__(
|
|
self,
|
|
task_id: str,
|
|
model: LLM,
|
|
work_dir: str, # 工作目录
|
|
max_chat_turns: int | None = settings.MAX_CHAT_TURNS, # 最大聊天次数,None表示无限制
|
|
max_retries: int | None = settings.MAX_RETRIES, # 最大反思次数,None表示无限制
|
|
code_interpreter: BaseCodeInterpreter | None = None,
|
|
context_window: int = 128000,
|
|
cancel_event: asyncio.Event | None = None,
|
|
) -> None:
|
|
super().__init__(task_id, model, context_window, cancel_event=cancel_event)
|
|
self.work_dir = work_dir
|
|
self.max_chat_turns = max_chat_turns
|
|
self.current_chat_turns = 0
|
|
self.max_retries = max_retries
|
|
self.is_first_run = True
|
|
self.system_prompt = CODER_PROMPT
|
|
self.code_interpreter = code_interpreter
|
|
|
|
async def run(self, prompt: str, subtask_title: str) -> CoderToWriter: # type: ignore[reportIncompatibleMethodOverride]
|
|
"""执行代码手子任务,生成并运行代码。
|
|
|
|
Args:
|
|
prompt: 子任务描述。
|
|
subtask_title: 子任务标题,用于分段输出。
|
|
|
|
Returns:
|
|
CoderToWriter 对象,包含代码执行结果和生成的图片列表。
|
|
"""
|
|
logger.info(f"{self.__class__.__name__}:开始:执行子任务: {subtask_title}")
|
|
assert self.code_interpreter is not None, "code_interpreter 未初始化"
|
|
self.code_interpreter.add_section(subtask_title)
|
|
|
|
# 根据 api_type 选择 tools 格式
|
|
api_type = self.model.api_type
|
|
tools = coder_tools_anthropic if api_type == ApiType.ANTHROPIC else coder_tools
|
|
|
|
# 如果是第一次运行,则添加系统提示
|
|
if self.is_first_run:
|
|
logger.info("首次运行,添加系统提示和数据集文件信息")
|
|
self.is_first_run = False
|
|
await self.append_chat_history(
|
|
{"role": "system", "content": self.system_prompt}
|
|
)
|
|
# 当前数据集文件
|
|
await self.append_chat_history(
|
|
{
|
|
"role": "user",
|
|
"content": f"当前文件夹下的数据集文件{get_current_files(self.work_dir, 'data')}",
|
|
}
|
|
)
|
|
|
|
# 添加 sub_task
|
|
logger.info(f"添加子任务提示: {prompt}")
|
|
await self.append_chat_history({"role": "user", "content": prompt})
|
|
|
|
retry_count = 0
|
|
last_error_message = ""
|
|
|
|
while True:
|
|
if self.max_retries is not None and retry_count >= self.max_retries:
|
|
logger.error(f"超过最大尝试次数: {self.max_retries}")
|
|
await redis_manager.publish_message(
|
|
self.task_id,
|
|
SystemMessage(content="超过最大尝试次数", type="error"),
|
|
)
|
|
logger.warning(f"任务失败,超过最大尝试次数{self.max_retries}, 最后错误信息: {last_error_message}")
|
|
return CoderToWriter(
|
|
code_response=f"任务失败,超过最大尝试次数{self.max_retries}, 最后错误信息: {last_error_message}",
|
|
created_images=[])
|
|
|
|
|
|
if self.max_chat_turns is not None and self.current_chat_turns >= self.max_chat_turns:
|
|
logger.error(f"超过最大聊天次数: {self.max_chat_turns}")
|
|
await redis_manager.publish_message(
|
|
self.task_id,
|
|
SystemMessage(content="超过最大聊天次数", type="error"),
|
|
)
|
|
raise Exception(
|
|
f"Reached maximum number of chat turns ({self.max_chat_turns}). Task incomplete."
|
|
)
|
|
|
|
self.current_chat_turns += 1
|
|
logger.info(f"当前对话轮次: {self.current_chat_turns}")
|
|
|
|
try:
|
|
response = await self._chat(
|
|
history=self.chat_history,
|
|
tools=tools,
|
|
tool_choice="auto",
|
|
agent_name=self.__class__.__name__,
|
|
)
|
|
|
|
# 如果有工具调用
|
|
if response.tool_calls:
|
|
logger.info("检测到工具调用")
|
|
tool_call = response.tool_calls[0]
|
|
tool_id = tool_call.id
|
|
|
|
if tool_call.name == "execute_code":
|
|
logger.info(f"调用工具: {tool_call.name}")
|
|
await redis_manager.publish_message(
|
|
self.task_id,
|
|
SystemMessage(
|
|
content=f"代码手调用{tool_call.name}工具"
|
|
),
|
|
)
|
|
|
|
code = json.loads(tool_call.arguments)["code"]
|
|
|
|
await redis_manager.publish_message(
|
|
self.task_id,
|
|
InterpreterMessage(
|
|
input={"code": code},
|
|
),
|
|
)
|
|
|
|
# 更新对话历史 - 添加助手的响应
|
|
assistant_msg: dict = {"role": "assistant", "content": response.content}
|
|
if response.reasoning_content:
|
|
assistant_msg["reasoning_content"] = response.reasoning_content
|
|
if response.tool_calls:
|
|
assistant_msg["tool_calls"] = [
|
|
{
|
|
"id": tc.id,
|
|
"type": "function",
|
|
"function": {"name": tc.name, "arguments": tc.arguments},
|
|
}
|
|
for tc in response.tool_calls
|
|
]
|
|
await self.append_chat_history(assistant_msg)
|
|
|
|
# 执行工具调用
|
|
logger.info("执行工具调用")
|
|
(
|
|
text_to_gpt,
|
|
error_occurred,
|
|
error_message,
|
|
) = await self.code_interpreter.execute_code(code)
|
|
|
|
# 添加工具执行结果
|
|
if error_occurred:
|
|
# 即使发生错误也要添加tool响应
|
|
await self.append_chat_history(
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": tool_id,
|
|
"name": "execute_code",
|
|
"content": error_message,
|
|
}
|
|
)
|
|
|
|
logger.warning(f"代码执行错误: {error_message}")
|
|
retry_count += 1
|
|
logger.info(f"当前尝试次:{retry_count} / {self.max_retries}")
|
|
last_error_message = error_message
|
|
reflection_prompt = get_reflection_prompt(error_message, code)
|
|
|
|
await redis_manager.publish_message(
|
|
self.task_id,
|
|
SystemMessage(content="代码手反思纠正错误", type="error"),
|
|
)
|
|
|
|
await self.append_chat_history(
|
|
{"role": "user", "content": reflection_prompt}
|
|
)
|
|
continue
|
|
else:
|
|
# 成功执行的tool响应
|
|
await self.append_chat_history(
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": tool_id,
|
|
"name": "execute_code",
|
|
"content": text_to_gpt,
|
|
}
|
|
)
|
|
# 成功执行后继续循环,等待下一步指令
|
|
continue
|
|
else:
|
|
# 没有工具调用,表示任务完成
|
|
logger.info("没有工具调用,任务完成")
|
|
return CoderToWriter(
|
|
code_response=response.content,
|
|
created_images=await self.code_interpreter.get_created_images(
|
|
subtask_title
|
|
),
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"执行过程中发生异常: {str(e)}")
|
|
retry_count += 1
|
|
last_error_message = str(e)
|
|
continue
|
|
logger.info(f"{self.__class__.__name__}:完成:执行子任务: {subtask_title}") |