diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63ac784
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.git
+.idea
diff --git a/.idea/agent_proj.iml b/.idea/agent_proj.iml
index b9ed519..5e376b1 100644
--- a/.idea/agent_proj.iml
+++ b/.idea/agent_proj.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 1511473..f86cb7f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/agent/chroma_db/chroma.sqlite3 b/agent/chroma_db/chroma.sqlite3
new file mode 100644
index 0000000..88acd6f
Binary files /dev/null and b/agent/chroma_db/chroma.sqlite3 differ
diff --git a/agent/react_agent.py b/agent/react_agent.py
new file mode 100644
index 0000000..4bb2f86
--- /dev/null
+++ b/agent/react_agent.py
@@ -0,0 +1,39 @@
+from langchain.agents import create_agent
+from model.factory import chat_model
+from utils.prompt_loader import load_system_prompts
+from agent.tools.agent_tools import (rag_summarize, get_weather, get_user_id,
+ get_user_location, get_current_month,
+ fill_context_for_report, fetch_external_data)
+from agent.tools.middleware import monitor_tool, log_before_model, repoet_prompt_switch
+
+
+class ReactAgent:
+ def __init__(self):
+ self.agent = create_agent(
+ model=chat_model,
+ system_prompt=load_system_prompts(),
+ tools=[rag_summarize, get_weather, get_user_location, get_user_id,
+ get_current_month, fetch_external_data, fill_context_for_report],
+ middleware=[monitor_tool, log_before_model, repoet_prompt_switch],
+ )
+
+ def excute_stream(self, query: str):
+ input_dict = {
+ "messages": [
+ {"role": "user", "content": query}
+ ]
+ }
+
+ # 上下文runtime信息,做提示词切换标记
+ response = self.agent.stream(input_dict, stream_mode="values", context={"report": False})
+ for chunk in response:
+ latest_message = chunk["messages"][-1]
+ if latest_message.content:
+ yield latest_message.content.strip() + "\n"
+
+
+if __name__ == '__main__':
+ agent = ReactAgent();
+ stream = agent.excute_stream("扫地机器人在我所在地的气温下如何保养")
+ for chunk in stream:
+ print(chunk, end="", flush=True)
diff --git a/agent/tools/__pycache__/agent_tools.cpython-312.pyc b/agent/tools/__pycache__/agent_tools.cpython-312.pyc
new file mode 100644
index 0000000..97c39ee
Binary files /dev/null and b/agent/tools/__pycache__/agent_tools.cpython-312.pyc differ
diff --git a/agent/tools/__pycache__/middleware.cpython-312.pyc b/agent/tools/__pycache__/middleware.cpython-312.pyc
new file mode 100644
index 0000000..37b237c
Binary files /dev/null and b/agent/tools/__pycache__/middleware.cpython-312.pyc differ
diff --git a/agent/tools/agent_tools.py b/agent/tools/agent_tools.py
new file mode 100644
index 0000000..603f9ed
--- /dev/null
+++ b/agent/tools/agent_tools.py
@@ -0,0 +1,99 @@
+import pandas
+from langchain_core.tools import tool
+from rag.rag_service import RagSummarizeService
+import random
+from utils.config_handler import agent_conf
+from utils.path_tool import get_abs_path
+from utils.logger_handler import logger
+import os
+
+rag = RagSummarizeService()
+
+user_ids = [str(i) for i in range(1001, 1011, 1)]
+month_arr = [f"2025-{str(i).zfill(2)}" for i in range(1, 13, 1)]
+external_data = {}
+
+
+@tool(description="向量存储中检索参考资料")
+def rag_summarize(query: str) -> str:
+ return rag.rag_summarize(query)
+
+
+@tool(description="获取指定城市天气,消息以字符串方式返回")
+def get_weather(city: str) -> str:
+ return f"城市{city}天气为晴天,温度为26摄氏度,湿度50%,南风1级,AQI21,最近6小时降雨概率极低"
+
+
+@tool(description="获取用户所在城市名称,以纯字符串形式返回")
+def get_user_location() -> str:
+ return random.choice(["深圳", "合肥", "杭州"])
+
+
+@tool(description="获取用户ID,以纯字符串形式返回")
+def get_user_id() -> str:
+ return random.choice(user_ids)
+
+
+@tool(description="获取当前月份,以纯字符串形式返回")
+def get_current_month() -> str:
+ return random.choice(month_arr)
+
+
+def generate_external_data():
+ """得到用户字典,
+ {
+ "user_id": {
+ "month": {"特征: ..., "效率:}
+ }
+ }"""
+
+ if not external_data:
+ external_data_path = agent_conf['external_data_path']
+ external_data_path = get_abs_path(external_data_path)
+
+ if not os.path.exists(external_data_path):
+ raise FileExistsError(f"外部文件{external_data_path}不存在")
+
+ with open(external_data_path, 'r', encoding='utf-8') as f:
+ for line in f.readlines():
+ arr = line.strip().split(',')
+ user_id = arr[0].replace('"', "")
+ feature = arr[1].replace('"', "")
+ efficiency = arr[2].replace('"', "")
+ consumables = arr[3].replace('"', "")
+ comparison = arr[4].replace('"', "")
+ time = arr[5].replace('"', "")
+
+ if user_id not in external_data:
+ external_data[user_id] = {}
+
+ external_data[user_id][time] = {
+ "特征": feature,
+ "效率": efficiency,
+ "耗材": consumables,
+ "对比": comparison,
+ }
+ return external_data
+
+
+@tool(description="从外部系统获取用户使用记录,以春字符串形式返回,如果未检索到,返回空字符串")
+def fetch_external_data(user_id: str, month: str) -> str:
+ generate_external_data()
+
+ try:
+ return external_data[user_id][month]
+ except KeyError:
+ logger.warning(f"[fetch_external_data]未检索到用户:{user_id}在{month}的使用数据")
+ return ""
+
+
+@tool(description="无入参,无返回值,调用后自动触发中间件,自动为报告生成动态场景上下文"
+ "+")
+def fill_context_for_report():
+ return "fill_context_for_report已调用"
+
+# if __name__ == '__main__':
+# user_id = "1005"
+# month = "2025-06"
+# res = fetch_external_data(user_id, month)
+# print(res)
diff --git a/agent/tools/chroma_db/chroma.sqlite3 b/agent/tools/chroma_db/chroma.sqlite3
new file mode 100644
index 0000000..3b39f56
Binary files /dev/null and b/agent/tools/chroma_db/chroma.sqlite3 differ
diff --git a/agent/tools/middleware.py b/agent/tools/middleware.py
new file mode 100644
index 0000000..d60abfe
--- /dev/null
+++ b/agent/tools/middleware.py
@@ -0,0 +1,51 @@
+from langchain.agents.middleware import wrap_tool_call, before_model, dynamic_prompt, ModelRequest
+from langchain.tools.tool_node import ToolCallRequest
+from typing import Callable
+from langchain_core.messages import ToolMessage
+from langgraph.types import Command
+from utils.logger_handler import logger
+from langchain.agents import AgentState
+from langgraph.runtime import Runtime
+from utils.prompt_loader import load_system_prompts, load_report_prompts
+
+@wrap_tool_call
+def monitor_tool(
+ # 函数
+ request: ToolCallRequest,
+ # 入参
+ handler: Callable[[ToolCallRequest], ToolMessage | Command],
+) -> ToolMessage | Command:
+ logger.info(f"[tool monitor]执行工具:{request.tool_call['name']}")
+ logger.info(f"[tool monitor]传入参数:{request.tool_call['args']}")
+
+ try:
+ result = handler(request)
+ logger.info(f"[tool minitor]工具{request.tool_call['name']}调用成功")
+
+ if request.tool_call['name'] == 'fill_context_for_report':
+ request.runtime.context["report"] = True
+
+ return result
+ except Exception as e:
+ logger.error(f"工具{request.tool_call['name']}调用失败,原因: {str(e)}")
+ raise e
+
+
+@before_model
+def log_before_model(
+ state: AgentState, # 状态记录
+ runtine: Runtime, # 执行过程 上下文信息
+):
+ logger.info(f"[log_before_model]即将调用模型,带有{len(state["messages"])}条消息")
+ logger.debug(f"[log_before_model]{type(state["messages"][-1]).__name__} | "
+ f"消息内容:{state["messages"][-1].content.strip()}")
+ return None
+
+
+@dynamic_prompt # 每一次提示词生成前,调用
+def repoet_prompt_switch(request: ModelRequest):
+ is_report = request.runtime.context.get("report", False)
+ if is_report:
+ return load_report_prompts()
+
+ return load_system_prompts()
diff --git a/config/agent.yaml b/config/agent.yaml
index e69de29..eb994ba 100644
--- a/config/agent.yaml
+++ b/config/agent.yaml
@@ -0,0 +1 @@
+external_data_path: data/external/records.csv
diff --git a/logs/Agent_20260301.log b/logs/Agent_20260301.log
new file mode 100644
index 0000000..600704c
--- /dev/null
+++ b/logs/Agent_20260301.log
@@ -0,0 +1,28 @@
+2026-03-01 00:10:11,186 - Agent - WARNING - agent_tools.py:86 - [fetch_external_data]未检索到用户:1004在2月的使用数据
+2026-03-01 00:10:15,880 - Agent - WARNING - agent_tools.py:86 - [fetch_external_data]未检索到用户:1008在8月的使用数据
+2026-03-01 11:06:16,324 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有1条消息
+2026-03-01 11:06:16,324 - Agent - DEBUG - middleware.py:40 - [log_before_model]HumanMessage | 消息内容:扫地机器人在我所在地的气温下如何保养
+2026-03-01 11:06:50,251 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有1条消息
+2026-03-01 11:06:50,251 - Agent - DEBUG - middleware.py:40 - [log_before_model]HumanMessage | 消息内容:扫地机器人在我所在地的气温下如何保养
+2026-03-01 11:08:04,788 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有1条消息
+2026-03-01 11:08:04,789 - Agent - DEBUG - middleware.py:40 - [log_before_model]HumanMessage | 消息内容:扫地机器人在我所在地的气温下如何保养
+2026-03-01 11:08:09,924 - Agent - INFO - middleware.py:18 - [tool monitor]执行工具:get_user_location
+2026-03-01 11:08:09,925 - Agent - INFO - middleware.py:19 - [tool monitor]传入参数:{}
+2026-03-01 11:08:09,926 - Agent - INFO - middleware.py:23 - [tool minitor]工具get_user_location调用成功
+2026-03-01 11:08:09,927 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有3条消息
+2026-03-01 11:08:09,927 - Agent - DEBUG - middleware.py:40 - [log_before_model]ToolMessage | 消息内容:杭州
+2026-03-01 11:08:13,076 - Agent - INFO - middleware.py:18 - [tool monitor]执行工具:get_weather
+2026-03-01 11:08:13,076 - Agent - INFO - middleware.py:19 - [tool monitor]传入参数:{'city': '杭州'}
+2026-03-01 11:08:13,078 - Agent - INFO - middleware.py:23 - [tool minitor]工具get_weather调用成功
+2026-03-01 11:08:13,079 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有5条消息
+2026-03-01 11:08:13,079 - Agent - DEBUG - middleware.py:40 - [log_before_model]ToolMessage | 消息内容:城市杭州天气为晴天,温度为26摄氏度,湿度50%,南风1级,AQI21,最近6小时降雨概率极低
+2026-03-01 11:08:16,346 - Agent - INFO - middleware.py:18 - [tool monitor]执行工具:rag_summarize
+2026-03-01 11:08:16,346 - Agent - INFO - middleware.py:19 - [tool monitor]传入参数:{'query': '扫地机器人在26摄氏度温度和50%湿度环境下的保养方法'}
+2026-03-01 11:08:19,183 - Agent - INFO - middleware.py:23 - [tool minitor]工具rag_summarize调用成功
+2026-03-01 11:08:19,185 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有7条消息
+2026-03-01 11:08:19,185 - Agent - DEBUG - middleware.py:40 - [log_before_model]ToolMessage | 消息内容:参考资料中未提及扫地机器人在26摄氏度温度和50%湿度环境下的具体保养方法。
+2026-03-01 11:08:21,538 - Agent - INFO - middleware.py:18 - [tool monitor]执行工具:rag_summarize
+2026-03-01 11:08:21,539 - Agent - INFO - middleware.py:19 - [tool monitor]传入参数:{'query': '扫地机器人日常保养方法'}
+2026-03-01 11:08:25,493 - Agent - INFO - middleware.py:23 - [tool minitor]工具rag_summarize调用成功
+2026-03-01 11:08:25,494 - Agent - INFO - middleware.py:39 - [log_before_model]即将调用模型,带有9条消息
+2026-03-01 11:08:25,494 - Agent - DEBUG - middleware.py:40 - [log_before_model]ToolMessage | 消息内容:定期清理滚刷和边刷上的缠绕物,清空尘盒,擦拭传感器和充电触点,检查并清理滤网,避免在潮湿或有水区域使用,及时更换磨损配件。
diff --git a/middleware.py b/middleware.py
deleted file mode 100644
index e69de29..0000000
diff --git a/rag/__pycache__/rag_service.cpython-312.pyc b/rag/__pycache__/rag_service.cpython-312.pyc
new file mode 100644
index 0000000..674b120
Binary files /dev/null and b/rag/__pycache__/rag_service.cpython-312.pyc differ
diff --git a/rag/__pycache__/vector_store.cpython-312.pyc b/rag/__pycache__/vector_store.cpython-312.pyc
new file mode 100644
index 0000000..d2480ba
Binary files /dev/null and b/rag/__pycache__/vector_store.cpython-312.pyc differ
diff --git a/utils/__pycache__/file_handler.cpython-312.pyc b/utils/__pycache__/file_handler.cpython-312.pyc
index 6f64494..5a064df 100644
Binary files a/utils/__pycache__/file_handler.cpython-312.pyc and b/utils/__pycache__/file_handler.cpython-312.pyc differ
diff --git a/utils/__pycache__/logger_handler.cpython-312.pyc b/utils/__pycache__/logger_handler.cpython-312.pyc
index 218a339..c86f36c 100644
Binary files a/utils/__pycache__/logger_handler.cpython-312.pyc and b/utils/__pycache__/logger_handler.cpython-312.pyc differ
diff --git a/utils/__pycache__/prompt_loader.cpython-312.pyc b/utils/__pycache__/prompt_loader.cpython-312.pyc
new file mode 100644
index 0000000..267e267
Binary files /dev/null and b/utils/__pycache__/prompt_loader.cpython-312.pyc differ