Skip to content

Commit 4c1c976

Browse files
committed
git config core.ignorecase false
1 parent 2e784df commit 4c1c976

File tree

3 files changed

+723
-0
lines changed

3 files changed

+723
-0
lines changed

backend/app/core/agents.py

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
import json
2+
from app.core.llm import LLM
3+
from app.core.prompts import (
4+
get_completion_check_prompt,
5+
get_reflection_prompt,
6+
get_writer_prompt,
7+
CODER_PROMPT,
8+
MODELER_PROMPT,
9+
)
10+
from app.core.functions import tools
11+
from app.models.model import CoderToWriter
12+
from app.models.user_output import UserOutput
13+
from app.utils.enums import CompTemplate, FormatOutPut
14+
from app.utils.log_util import logger
15+
from app.config.setting import settings
16+
from app.tools.code_interpreter import E2BCodeInterpreter
17+
from app.utils.common_utils import get_current_files
18+
from app.utils.redis_manager import redis_manager
19+
from app.schemas.response import SystemMessage
20+
21+
22+
class Agent:
23+
def __init__(
24+
self,
25+
task_id: str,
26+
model: LLM,
27+
max_chat_turns: int = 30, # 单个agent最大对话轮次
28+
user_output: UserOutput = None,
29+
) -> None:
30+
self.task_id = task_id
31+
self.model = model
32+
self.chat_history: list[dict] = [] # 存储对话历史
33+
self.max_chat_turns = max_chat_turns # 最大对话轮次
34+
self.current_chat_turns = 0 # 当前对话轮次计数器
35+
self.user_output = user_output
36+
37+
async def run(self, prompt: str, system_prompt: str) -> str:
38+
"""
39+
执行agent的对话并返回结果和总结
40+
41+
Args:
42+
prompt: 输入的提示
43+
44+
Returns:
45+
str: 模型的响应
46+
"""
47+
try:
48+
logger.info(f"{self.__class__.__name__}:开始:执行对话")
49+
self.current_chat_turns = 0 # 重置对话轮次计数器
50+
51+
# 更新对话历史
52+
self.append_chat_history({"role": "system", "content": system_prompt})
53+
self.append_chat_history({"role": "user", "content": prompt})
54+
55+
# 获取历史消息用于本次对话
56+
response = await self.model.chat(
57+
history=self.chat_history, agent_name=self.__class__.__name__
58+
)
59+
response_content = response.choices[0].message.content
60+
self.chat_history.append({"role": "assistant", "content": response_content})
61+
logger.info(f"{self.__class__.__name__}:完成:执行对话")
62+
return response_content
63+
except Exception as e:
64+
error_msg = f"执行过程中遇到错误: {str(e)}"
65+
logger.error(f"Agent执行失败: {str(e)}")
66+
return error_msg
67+
68+
def append_chat_history(self, msg: dict) -> None:
69+
self.chat_history.append(msg)
70+
# self.user_output.data_recorder.append_chat_history(
71+
# msg, agent_name=self.__class__.__name__
72+
# )
73+
74+
75+
class ModelerAgent(Agent): # 继承自Agent类而不是BaseModel
76+
def __init__(
77+
self,
78+
model: LLM,
79+
max_chat_turns: int = 30, # 添加最大对话轮次限制
80+
) -> None:
81+
super().__init__(model, max_chat_turns)
82+
self.system_prompt = MODELER_PROMPT
83+
84+
85+
# 代码强
86+
class CoderAgent(Agent): # 同样继承自Agent类
87+
def __init__(
88+
self,
89+
task_id: str,
90+
model: LLM,
91+
work_dir: str, # 工作目录
92+
max_chat_turns: int = settings.MAX_CHAT_TURNS, # 最大聊天次数
93+
max_retries: int = settings.MAX_RETRIES, # 最大反思次数
94+
code_interpreter: E2BCodeInterpreter = None,
95+
) -> None:
96+
super().__init__(task_id, model, max_chat_turns)
97+
self.work_dir = work_dir
98+
self.max_retries = max_retries
99+
self.is_first_run = True
100+
self.system_prompt = CODER_PROMPT
101+
self.code_interpreter = code_interpreter
102+
103+
async def run(self, prompt: str, subtask_title: str) -> CoderToWriter:
104+
logger.info(f"{self.__class__.__name__}:开始:执行子任务: {subtask_title}")
105+
self.code_interpreter.add_section(subtask_title)
106+
107+
# 如果是第一次运行,则添加系统提示
108+
if self.is_first_run:
109+
self.is_first_run = False
110+
self.append_chat_history({"role": "system", "content": self.system_prompt})
111+
# 当前数据集文件
112+
self.append_chat_history(
113+
{
114+
"role": "user",
115+
"content": f"当前文件夹下的数据集文件{get_current_files(self.work_dir, 'data')}",
116+
}
117+
)
118+
119+
self.append_chat_history({"role": "user", "content": prompt})
120+
121+
retry_count = 0
122+
last_error_message = ""
123+
task_completed = False
124+
125+
if self.current_chat_turns >= self.max_chat_turns:
126+
await redis_manager.publish_message(
127+
self.task_id,
128+
SystemMessage(content="超过最大思考次数", type="error"),
129+
)
130+
raise Exception(
131+
f"Reached maximum number of chat turns ({self.max_chat_turns}). Task incomplete."
132+
)
133+
134+
if retry_count >= self.max_retries:
135+
await redis_manager.publish_message(
136+
self.task_id,
137+
SystemMessage(content="超过最大尝试次数", type="error"),
138+
)
139+
raise Exception(
140+
f"Failed to complete task after {self.max_retries} attempts. Last error: {last_error_message}"
141+
)
142+
143+
# try:
144+
while (
145+
not task_completed
146+
and retry_count < self.max_retries
147+
and self.current_chat_turns < self.max_chat_turns
148+
):
149+
self.current_chat_turns += 1
150+
response = await self.model.chat(
151+
history=self.chat_history,
152+
tools=tools,
153+
tool_choice="auto",
154+
agent_name=self.__class__.__name__,
155+
)
156+
157+
if (
158+
hasattr(response.choices[0].message, "tool_calls")
159+
and response.choices[0].message.tool_calls
160+
):
161+
tool_call = response.choices[0].message.tool_calls[0]
162+
tool_id = tool_call.id
163+
# TODO: json JSON解析时遇到了无效的转义字符
164+
if tool_call.function.name == "execute_code":
165+
await redis_manager.publish_message(
166+
self.task_id,
167+
SystemMessage(
168+
content=f"代码手调用{tool_call.function.name}工具"
169+
),
170+
)
171+
code = json.loads(tool_call.function.arguments)["code"]
172+
full_content = response.choices[0].message.content
173+
# 更新对话历史 - 添加助手的响应
174+
self.append_chat_history(
175+
{
176+
"role": "assistant",
177+
"content": full_content,
178+
"tool_calls": [
179+
{
180+
"id": tool_id,
181+
"type": "function",
182+
"function": {
183+
"name": "execute_code",
184+
"arguments": json.dumps({"code": code}),
185+
},
186+
}
187+
],
188+
}
189+
)
190+
191+
# 执行工具调用
192+
(
193+
text_to_gpt,
194+
error_occurred,
195+
error_message,
196+
) = await self.code_interpreter.execute_code(code)
197+
198+
# 记录执行结果
199+
200+
# 添加工具执行结果
201+
self.append_chat_history(
202+
{
203+
"role": "tool",
204+
"content": text_to_gpt,
205+
"tool_call_id": tool_id,
206+
}
207+
)
208+
209+
if error_occurred:
210+
retry_count += 1
211+
last_error_message = error_message
212+
reflection_prompt = get_reflection_prompt(error_message, code)
213+
214+
await redis_manager.publish_message(
215+
self.task_id,
216+
SystemMessage(content="代码手反思错误", type="error"),
217+
)
218+
219+
self.append_chat_history(
220+
{"role": "user", "content": reflection_prompt}
221+
)
222+
continue
223+
224+
# 检查任务完成情况时也计入对话轮次
225+
self.current_chat_turns += 1
226+
# 使用所有执行结果生成检查提示
227+
completion_check_prompt = get_completion_check_prompt(
228+
prompt, text_to_gpt
229+
)
230+
self.append_chat_history(
231+
{"role": "user", "content": completion_check_prompt}
232+
)
233+
234+
completion_response = await self.model.chat(
235+
history=self.chat_history,
236+
tools=tools,
237+
tool_choice="auto",
238+
agent_name=self.__class__.__name__,
239+
)
240+
241+
# # TODO: 压缩对话历史
242+
243+
## 没有调用工具,代表已经完成了
244+
if not (
245+
hasattr(completion_response.choices[0].message, "tool_calls")
246+
and completion_response.choices[0].message.tool_calls
247+
):
248+
task_completed = True
249+
return completion_response.choices[0].message.content
250+
251+
if retry_count >= self.max_retries:
252+
return f"Failed to complete task after {self.max_retries} attempts. Last error: {last_error_message}"
253+
254+
if self.current_chat_turns >= self.max_chat_turns:
255+
return f"Reached maximum number of chat turns ({self.max_chat_turns}). Task incomplete."
256+
257+
logger.info(f"{self.__class__.__name__}:完成:执行子任务: {subtask_title}")
258+
259+
return response.choices[0].message.content
260+
261+
262+
# 长文本
263+
# TODO: 并行 parallel
264+
# TODO: 获取当前文件下的文件
265+
class WriterAgent(Agent): # 同样继承自Agent类
266+
def __init__(
267+
self,
268+
task_id: str,
269+
model: LLM,
270+
max_chat_turns: int = 10, # 添加最大对话轮次限制
271+
comp_template: CompTemplate = CompTemplate,
272+
format_output: FormatOutPut = FormatOutPut.Markdown,
273+
user_output: UserOutput = None,
274+
) -> None:
275+
super().__init__(task_id, model, max_chat_turns, user_output)
276+
self.format_out_put = format_output
277+
self.comp_template = comp_template
278+
self.system_prompt = get_writer_prompt(format_output)
279+
self.available_images: list[str] = []
280+
281+
async def run(
282+
self,
283+
prompt: str,
284+
available_images: list[str] = None,
285+
static_prefix: str = "/static/",
286+
) -> str:
287+
"""
288+
执行写作任务
289+
Args:
290+
prompt: 写作提示
291+
available_images: 可用的图片相对路径列表(如 20250420-173744-9f87792c/编号_分布.png)
292+
static_prefix: 静态资源前缀
293+
"""
294+
if available_images:
295+
self.available_images = available_images
296+
# 拼接成完整URL
297+
image_list = "\n".join(
298+
[
299+
f"- {static_prefix}{img if img.startswith('/') else '/' + img}"
300+
for img in available_images
301+
]
302+
)
303+
image_prompt = f"\n可用的图片链接列表:\n{image_list}\n请在写作时适当引用这些图片链接。"
304+
prompt = prompt + image_prompt
305+
306+
return await super().run(prompt, self.system_prompt)
307+
308+
async def summarize(self) -> str:
309+
"""
310+
总结对话内容
311+
"""
312+
try:
313+
self.append_chat_history(
314+
{"role": "user", "content": "请简单总结以上完成什么任务取得什么结果:"}
315+
)
316+
# 获取历史消息用于本次对话
317+
response = await self.model.chat(
318+
history=self.chat_history, agent_name=self.__class__.__name__
319+
)
320+
self.append_chat_history(
321+
{"role": "assistant", "content": response.choices[0].message.content}
322+
)
323+
return response.choices[0].message.content
324+
except Exception as e:
325+
logger.error(f"总结生成失败: {str(e)}")
326+
# 返回一个基础总结,避免完全失败
327+
return "由于网络原因无法生成详细总结,但已完成主要任务处理。"

0 commit comments

Comments
 (0)