项目代码格式化

This commit is contained in:
shell committed 2026-11-01 21:38:30 +08:00
1 parent 2c1bac9bcb
commit cfb7b52fbd
6 files changed
+112 -76

No files matched your search

+5
View File
@@ -20,3 +20,8 @@ mini-agent = "mini_agent:main"
[build-system] [build-system]
requires = ["uv_build>=0.13.6,<0.14.0"] requires = ["uv_build>=0.13.6,<0.14.0"]
build-backend = "uv_build" build-backend = "uv_build"
[dependency-groups]
dev = [
"ruff>=0.17.3",
]
+22 -27
View File
@@ -2,15 +2,16 @@ import json
import os import os
from typing import Any from typing import Any
from mini_agent.tools.tavily_search import TavilySearch from dotenv import load_dotenv
from openai import OpenAI from openai import OpenAI
from mini_agent.tools.tool_search import ToolSearch from mini_agent.tools.tavily_search import TavilySearch
from mini_agent.tools.tool_manager import ToolManager from mini_agent.tools.tool_manager import ToolManager
from dotenv import load_dotenv from mini_agent.tools.tool_search import ToolSearch
load_dotenv() load_dotenv()
class ReActAgent: class ReActAgent:
SYSTEM_PROMPT = """ SYSTEM_PROMPT = """
你是一个能够调用外部工具的智能助手。 你是一个能够调用外部工具的智能助手。
@@ -51,9 +52,7 @@ class ReActAgent:
self.timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60)) self.timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60))
if not all([self.model, self.base_url, self.api_key]): if not all([self.model, self.base_url, self.api_key]):
raise ValueError( raise ValueError("模型ID、API密钥和服务地址必须被提供或在.env文件中定义。")
"模型ID、API密钥和服务地址必须被提供或在.env文件中定义。"
)
self.client = OpenAI( self.client = OpenAI(
api_key=self.api_key, api_key=self.api_key,
@@ -67,7 +66,7 @@ class ReActAgent:
self.search_tool = ToolSearch(self.tool_manager) self.search_tool = ToolSearch(self.tool_manager)
self.tool_manager.register(self.search_tool) self.tool_manager.register(self.search_tool)
web_search_tool = TavilySearch() web_search_tool = TavilySearch()
web_search_tool.config(**{"api_key":os.getenv("TAVILY_KEY","")}) web_search_tool.config(**{"api_key": os.getenv("TAVILY_KEY", "")})
self.tool_manager.register(web_search_tool) self.tool_manager.register(web_search_tool)
def run(self, question: str) -> str: def run(self, question: str) -> str:
@@ -78,12 +77,9 @@ class ReActAgent:
available_tools = [self.search_tool] available_tools = [self.search_tool]
tool_calls_count = 0 tool_calls_count = 0
messages = [ messages = [{"role": "user", "content": question}]
{"role":"user","content":question}
]
for step in range(self.max_steps): for step in range(self.max_steps):
print(f"\n========== Step {step + 1} ==========") print(f"\n========== Step {step + 1} ==========")
tools = [self.tool_manager.tool_to_openai(tool) for tool in available_tools] tools = [self.tool_manager.tool_to_openai(tool) for tool in available_tools]
@@ -105,34 +101,31 @@ class ReActAgent:
raise RuntimeError("模型没有返回有效内容") raise RuntimeError("模型没有返回有效内容")
# 保存模型的工具调用消息 # 保存模型的工具调用消息
messages.append( messages.append(message.model_dump(exclude_none=True))
message.model_dump(exclude_none=True)
)
for tool_call in message.tool_calls: for tool_call in message.tool_calls:
tool_name = tool_call.function.name tool_name = tool_call.function.name
try: try:
arguments = json.loads( arguments = json.loads(tool_call.function.arguments or "{}")
tool_call.function.arguments or "{}"
)
except json.JSONDecodeError: except json.JSONDecodeError:
observation = json.dumps( observation = json.dumps(
{"error": "工具参数不是合法的 JSON"}, {"error": "工具参数不是合法的 JSON"},
ensure_ascii=False, ensure_ascii=False,
) )
messages.append({ messages.append(
{
"role": "tool", "role": "tool",
"tool_call_id": tool_call.id, "tool_call_id": tool_call.id,
"content": observation, "content": observation,
}) }
)
continue continue
print(f"调用工具: {tool_name}") print(f"调用工具: {tool_name}")
print(f"参数: {arguments}") print(f"参数: {arguments}")
observation = self.tool_manager.call_tool(tool_name, arguments)
observation = self.tool_manager.call_tool(tool_name,arguments)
# 更新当前可用工具 # 更新当前可用工具
if tool_name == self.search_tool.name: if tool_name == self.search_tool.name:
new_tools = [] new_tools = []
@@ -140,14 +133,16 @@ class ReActAgent:
if not any(tool.name == item.name for tool in available_tools): if not any(tool.name == item.name for tool in available_tools):
available_tools.append(item) available_tools.append(item)
new_tools.append(item) new_tools.append(item)
observation = [self.tool_manager.tool_to_openai(tool) for tool in new_tools] observation = [
self.tool_manager.tool_to_openai(tool) for tool in new_tools
]
messages.append({ messages.append(
{
"role": "tool", "role": "tool",
"tool_call_id": tool_call.id, "tool_call_id": tool_call.id,
"content": str(observation), "content": str(observation),
}) }
raise RuntimeError(
f"Agent 已达到最大执行步数 ({self.max_steps}),任务未完成"
) )
raise RuntimeError(f"Agent 已达到最大执行步数 ({self.max_steps}),任务未完成")
+24 -20
View File
@@ -1,26 +1,28 @@
from mini_agent.tools.tool import Configuration, Tool,Parameter
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from tavily import TavilyClient from tavily import TavilyClient
from mini_agent.tools.tool import Configuration, Parameter, Tool
class ResultItem(BaseModel): class ResultItem(BaseModel):
url:str url: str
title:str title: str
content:str content: str
score:float score: float
raw_content:str | None = None raw_content: str | None = None
id:str id: str
class SearchResult(BaseModel): class SearchResult(BaseModel):
query:str query: str
answer:str | None answer: str | None
images:list[str] = [] images: list[str] = []
results: list[ResultItem] results: list[ResultItem]
class TavilySearch(Tool[SearchResult]):
class TavilySearch(Tool[SearchResult]):
@property @property
def name(self)->str: def name(self) -> str:
return "Tavily网络搜索工具 " return "Tavily网络搜索工具 "
@property @property
@@ -36,25 +38,27 @@ class TavilySearch(Tool[SearchResult]):
@property @property
def configurations(self) -> list[Configuration]: def configurations(self) -> list[Configuration]:
return [ return [Configuration(name="api_key", type="str", required=True)]
Configuration(name="api_key",type="str",required=True)
]
@property @property
def parameters(self) -> list[Parameter]: def parameters(self) -> list[Parameter]:
return [ return [
Parameter(name="query",type="str",description="query keyword",required=True) Parameter(
name="query", type="str", description="query keyword", required=True
)
] ]
def config(self,**kwargs) -> None: def config(self, **kwargs) -> None:
api_key = kwargs.get("api_key",None) api_key = kwargs.get("api_key", None)
if not api_key: if not api_key:
raise ValueError("API Key is required") raise ValueError("API Key is required")
self.client = TavilyClient(api_key=api_key) self.client = TavilyClient(api_key=api_key)
def run(self, **kwargs) -> SearchResult: def run(self, **kwargs) -> SearchResult:
query = kwargs.get("query",None) query = kwargs.get("query", None)
if not query: if not query:
raise ValueError("查询参数不能为空") raise ValueError("查询参数不能为空")
response = self.client.search(query=query,search_depth="advanced",include_answer="advanced") response = self.client.search(
query=query, search_depth="advanced", include_answer="advanced"
)
return SearchResult.model_validate(response) return SearchResult.model_validate(response)
+18 -16
View File
@@ -1,49 +1,51 @@
from abc import ABC, abstractmethod
from pydantic import BaseModel from pydantic import BaseModel
from abc import ABC,abstractmethod
class Parameter(BaseModel): class Parameter(BaseModel):
name:str name: str
type:str type: str
description:str description: str
required:bool =True required: bool = True
class Configuration(BaseModel): class Configuration(BaseModel):
name:str name: str
type:str type: str
required:bool = True required: bool = True
class Tool[T](ABC): class Tool[T](ABC):
@property @property
@abstractmethod @abstractmethod
def name(self)->str: def name(self) -> str:
pass pass
@property @property
@abstractmethod @abstractmethod
def description(self)->str: def description(self) -> str:
pass pass
@property @property
@abstractmethod @abstractmethod
def catalog(self)->str: def catalog(self) -> str:
pass pass
@property @property
@abstractmethod @abstractmethod
def parameters(self)->list[Parameter]: def parameters(self) -> list[Parameter]:
pass pass
@property @property
@abstractmethod @abstractmethod
def configurations(self)->list[Configuration]: def configurations(self) -> list[Configuration]:
pass pass
@abstractmethod @abstractmethod
def config(self,**kwargs)->None: def config(self, **kwargs) -> None:
pass pass
@abstractmethod @abstractmethod
def run(self,**kwargs)->T: def run(self, **kwargs) -> T:
pass pass
+10 -13
View File
@@ -4,19 +4,20 @@ from mini_agent.tools.tool import Tool
class ToolManager: class ToolManager:
_tools: dict[str, Tool] = {}
_tools:dict[str,Tool] = {} def register[T](self, tool: Tool[T]) -> None:
def register[T](self,tool:Tool[T])->None:
if tool.name in self._tools: if tool.name in self._tools:
raise ValueError(f"{tool.name} is already registered") raise ValueError(f"{tool.name} is already registered")
self._tools[tool.name] = tool self._tools[tool.name] = tool
def get_tools(self,keyword:str,catalog:str | None)->list[Tool]: def get_tools(self, keyword: str, catalog: str | None) -> list[Tool]:
return list(self._tools.values()) return list(self._tools.values())
def get_tools_for_openai(self,keyword:str,catalog:str|None)->list[dict[str,Any]]: def get_tools_for_openai(
tools = self.get_tools(keyword,catalog) self, keyword: str, catalog: str | None
) -> list[dict[str, Any]]:
tools = self.get_tools(keyword, catalog)
result = [] result = []
for tool in tools: for tool in tools:
result.append(self.tool_to_openai(tool)) result.append(self.tool_to_openai(tool))
@@ -30,7 +31,7 @@ class ToolManager:
return tool.run(**arguments) return tool.run(**arguments)
def tool_to_openai[T](self,tool:Tool[T])->dict[str,Any]: def tool_to_openai[T](self, tool: Tool[T]) -> dict[str, Any]:
type_mapping = { type_mapping = {
"str": "string", "str": "string",
"string": "string", "string": "string",
@@ -49,18 +50,14 @@ class ToolManager:
json_type = type_mapping.get(parameter.type) json_type = type_mapping.get(parameter.type)
if json_type is None: if json_type is None:
raise ValueError( raise ValueError(f"不支持的参数类型: {parameter.type}")
f"不支持的参数类型: {parameter.type}"
)
properties[parameter.name] = { properties[parameter.name] = {
"type": json_type, "type": json_type,
} }
if parameter.description: if parameter.description:
properties[parameter.name]["description"] = ( properties[parameter.name]["description"] = parameter.description
parameter.description
)
if parameter.required: if parameter.required:
required.append(parameter.name) required.append(parameter.name)
Generated
+33
View File
@@ -219,6 +219,11 @@ dependencies = [
{ name = "tavily-python" }, { name = "tavily-python" },
] ]
[package.dev-dependencies]
dev = [
{ name = "ruff" },
]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "openai", specifier = ">=3.42.0" }, { name = "openai", specifier = ">=3.42.0" },
@@ -227,6 +232,9 @@ requires-dist = [
{ name = "tavily-python", specifier = ">=0.8.5" }, { name = "tavily-python", specifier = ">=0.8.5" },
] ]
[package.metadata.requires-dev]
dev = [{ name = "ruff", specifier = ">=0.17.3" }]
[[package]] [[package]]
name = "openai" name = "openai"
version = "3.42.0" version = "3.42.0"
@@ -364,6 +372,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
] ]
[[package]]
name = "ruff"
version = "0.17.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" },
{ url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" },
{ url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" },
{ url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" },
{ url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" },
{ url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" },
{ url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" },
{ url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" },
{ url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" },
{ url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" },
{ url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" },
{ url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" },
{ url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" },
{ url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" },
{ url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" },
{ url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" },
]
[[package]] [[package]]
name = "sniffio" name = "sniffio"
version = "1.3.1" version = "1.3.1"