react_agent/ agent_tools/middleware

This commit is contained in:
HengZhang 2026-03-01 11:09:18 +08:00
parent 56b276eb3f
commit 187b8c8756
18 changed files with 222 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.git
.idea

View File

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="RAG" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="edu" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.12 (Eula)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="RAG" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="edu" project-jdk-type="Python SDK" />
</project>

Binary file not shown.

39
agent/react_agent.py Normal file
View File

@ -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)

Binary file not shown.

Binary file not shown.

View File

@ -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)

Binary file not shown.

51
agent/tools/middleware.py Normal file
View File

@ -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()

View File

@ -0,0 +1 @@
external_data_path: data/external/records.csv

28
logs/Agent_20260301.log Normal file
View File

@ -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 | 消息内容:定期清理滚刷和边刷上的缠绕物,清空尘盒,擦拭传感器和充电触点,检查并清理滤网,避免在潮湿或有水区域使用,及时更换磨损配件。

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.