diff --git a/apps/constants.py b/apps/constants.py
index 1303e3d66e9c91e26f2c4e4272aca54ece18434b..9c02fd8da5a09d1e699b5dbdb03f914a3292a727 100644
--- a/apps/constants.py
+++ b/apps/constants.py
@@ -51,5 +51,11 @@ ALLOWED_ICON_MIME_TYPES = [
MCP_PATH = Path(config.deploy.data_dir) / "semantics" / "mcp"
# 项目路径
PROJ_PATH = Path(__file__).parent.parent
-# 图标存储
+# 图标存储位置
ICON_PATH = PROJ_PATH / "static" / "icons"
+# MCP Agent 最大重试次数
+AGENT_MAX_RETRY_TIMES = 3
+# MCP Agent 最大步骤数
+AGENT_MAX_STEPS = 25
+# MCP Agent 最终步骤名称
+AGENT_FINAL_STEP_NAME = "FIANL"
diff --git a/apps/llm/function.py b/apps/llm/function.py
index b6766e49d4fcd9a679ddb5bc9d679bfec05323fa..491087916fb6aad8f1283be1843777f399157c4a 100644
--- a/apps/llm/function.py
+++ b/apps/llm/function.py
@@ -25,6 +25,7 @@ class FunctionLLM:
"""用于FunctionCall的模型"""
timeout: float = 30.0
+ config: LLMData
def __init__(self, llm_config: LLMData | None = None) -> None:
"""
@@ -43,34 +44,34 @@ class FunctionLLM:
logger.error(err)
raise RuntimeError(err)
- self._config: LLMData = llm_config
+ self.config: LLMData = llm_config
self._params = {
- "model": self._config.modelName,
+ "model": self.config.modelName,
"messages": [],
}
- if self._config.functionCallBackend == FunctionCallBackend.OLLAMA and not self._config.openaiAPIKey:
+ if self.config.functionCallBackend == FunctionCallBackend.OLLAMA and not self.config.openaiAPIKey:
self._client = ollama.AsyncClient(
- host=self._config.openaiBaseUrl,
+ host=self.config.openaiBaseUrl,
timeout=self.timeout,
)
- elif self._config.functionCallBackend == FunctionCallBackend.OLLAMA and self._config.openaiAPIKey:
+ elif self.config.functionCallBackend == FunctionCallBackend.OLLAMA and self.config.openaiAPIKey:
self._client = ollama.AsyncClient(
- host=self._config.openaiBaseUrl,
+ host=self.config.openaiBaseUrl,
headers={
- "Authorization": f"Bearer {self._config.openaiAPIKey}",
+ "Authorization": f"Bearer {self.config.openaiAPIKey}",
},
timeout=self.timeout,
)
- elif self._config.functionCallBackend != FunctionCallBackend.OLLAMA and not self._config.openaiAPIKey:
+ elif self.config.functionCallBackend != FunctionCallBackend.OLLAMA and not self.config.openaiAPIKey:
self._client = openai.AsyncOpenAI(
- base_url=self._config.openaiBaseUrl,
+ base_url=self.config.openaiBaseUrl,
timeout=self.timeout,
)
- elif self._config.functionCallBackend != FunctionCallBackend.OLLAMA and self._config.openaiAPIKey:
+ elif self.config.functionCallBackend != FunctionCallBackend.OLLAMA and self.config.openaiAPIKey:
self._client = openai.AsyncOpenAI(
- base_url=self._config.openaiBaseUrl,
- api_key=self._config.openaiAPIKey,
+ base_url=self.config.openaiBaseUrl,
+ api_key=self.config.openaiAPIKey,
timeout=self.timeout,
)
@@ -98,14 +99,14 @@ class FunctionLLM:
"temperature": temperature,
})
- if self._config.functionCallBackend == FunctionCallBackend.VLLM:
+ if self.config.functionCallBackend == FunctionCallBackend.VLLM:
self._params["extra_body"] = {"guided_json": schema}
- elif self._config.functionCallBackend == FunctionCallBackend.JSON_MODE:
+ elif self.config.functionCallBackend == FunctionCallBackend.JSON_MODE:
logger.warning("[FunctionCall] json_mode无法确保输出格式符合要求,使用效果将受到影响")
self._params["response_format"] = {"type": "json_object"}
- elif self._config.functionCallBackend == FunctionCallBackend.STRUCTURED_OUTPUT:
+ elif self.config.functionCallBackend == FunctionCallBackend.STRUCTURED_OUTPUT:
self._params["response_format"] = {
"type": "json_schema",
"json_schema": {
@@ -116,7 +117,7 @@ class FunctionLLM:
},
}
- elif self._config.functionCallBackend == FunctionCallBackend.FUNCTION_CALL:
+ elif self.config.functionCallBackend == FunctionCallBackend.FUNCTION_CALL:
logger.warning("[FunctionCall] function_call无法确保一定调用工具,使用效果将受到影响")
self._params["tools"] = [
{
@@ -220,14 +221,14 @@ class FunctionLLM:
"""
# 检查max_tokens和temperature是否设置
if max_tokens is None:
- max_tokens = self._config.maxToken
+ max_tokens = self.config.maxToken
if temperature is None:
- temperature = self._config.temperature
+ temperature = self.config.temperature
- if self._config.functionCallBackend == FunctionCallBackend.OLLAMA:
+ if self.config.functionCallBackend == FunctionCallBackend.OLLAMA:
json_str = await self._call_ollama(messages, schema, max_tokens, temperature)
- elif self._config.functionCallBackend in [
+ elif self.config.functionCallBackend in [
FunctionCallBackend.FUNCTION_CALL,
FunctionCallBackend.JSON_MODE,
FunctionCallBackend.STRUCTURED_OUTPUT,
@@ -306,12 +307,14 @@ class JsonGenerator:
return {}
- def __init__(self, config: LLMData, query: str, conversation: list[dict[str, str]], schema: dict[str, Any]) -> None:
+ def __init__(
+ self, llm: FunctionLLM, query: str, conversation: list[dict[str, str]], schema: dict[str, Any],
+ ) -> None:
"""初始化JSON生成器"""
self._query = query
self._conversation = conversation
self._schema = schema
- self._config = config
+ self._llm = llm
self._trial = {}
self._count = 0
@@ -327,7 +330,7 @@ class JsonGenerator:
async def _assemble_message(self) -> str:
"""组装消息"""
# 检查类型
- function_call = self._config.functionCallBackend == FunctionCallBackend.FUNCTION_CALL
+ function_call = self._llm.config.functionCallBackend == FunctionCallBackend.FUNCTION_CALL
# 渲染模板
template = self._env.from_string(JSON_GEN_BASIC)
@@ -347,8 +350,7 @@ class JsonGenerator:
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt},
]
- function = FunctionLLM()
- return await function.call(messages, self._schema, max_tokens, temperature)
+ return await self._llm.call(messages, self._schema, max_tokens, temperature)
async def generate(self) -> dict[str, Any]:
diff --git a/apps/llm/patterns/__init__.py b/apps/llm/patterns/__init__.py
index 156953c901eb3388fce75f6e08547ee5ffb2fcc7..8a29c93cc645ac06dd121423c88de28e8f0d2a05 100644
--- a/apps/llm/patterns/__init__.py
+++ b/apps/llm/patterns/__init__.py
@@ -4,13 +4,9 @@
from apps.llm.patterns.core import CorePattern
from apps.llm.patterns.executor import (
ExecutorSummary,
- ExecutorThought,
)
-from apps.llm.patterns.select import Select
__all__ = [
"CorePattern",
"ExecutorSummary",
- "ExecutorThought",
- "Select",
]
diff --git a/apps/llm/patterns/executor.py b/apps/llm/patterns/executor.py
index ecb514a6ea0770cb05a81d7a7e2d153c0721949e..81861ab798326e023a1c2939da930c244ed65d2e 100644
--- a/apps/llm/patterns/executor.py
+++ b/apps/llm/patterns/executor.py
@@ -1,7 +1,7 @@
# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved.
"""使用大模型生成Executor的思考内容"""
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING
from apps.llm.reasoning import ReasoningLLM
from apps.llm.snippet import convert_context_to_prompt, facts_to_prompt
@@ -13,121 +13,6 @@ if TYPE_CHECKING:
from apps.schemas.scheduler import ExecutorBackground
-class ExecutorThought(CorePattern):
- """通过大模型生成Executor的思考内容"""
-
- @staticmethod
- def _default() -> tuple[dict[LanguageType, str], dict[LanguageType, str]]:
- """默认的Prompt内容"""
- return {
- LanguageType.CHINESE: r"You are a helpful assistant.",
- LanguageType.ENGLISH: r"You are a helpful assistant.",
- }, {
- LanguageType.CHINESE: r"""
-
-
- 你是一个可以使用工具的智能助手。
- 在回答用户的问题时,你为了获取更多的信息,使用了一个工具。
- 请简明扼要地总结工具的使用过程,提供你的见解,并给出下一步的行动。
-
- 注意:
- 工具的相关信息在标签中给出。
- 为了使你更好的理解发生了什么,你之前的思考过程在标签中给出。
- 输出时请不要包含XML标签,输出时请保持简明和清晰。
-
-
-
-
- {tool_name}
- {tool_description}
-
-
-
-
- {last_thought}
-
-
-
- 你当前需要解决的问题是:
- {user_question}
-
-
- 请综合以上信息,再次一步一步地进行思考,并给出见解和行动:
- """,
- LanguageType.ENGLISH: r"""
-
-
- You are an intelligent assistant who can use tools.
- When answering user questions, you use a tool to get more information.
- Please summarize the process of using the tool briefly, provide your insights, \
-and give the next action.
-
- Note:
- The information about the tool is given in the tag.
- To help you better understand what happened, your previous thought process is given in the \
- tag.
- Do not include XML tags in the output, and keep the output brief and clear.
-
-
-
-
- {tool_name}
- {tool_description}
-
-
-
-
- {last_thought}
-
-
-
- The question you need to solve is:
- {user_question}
-
-
- Please integrate the above information, think step by step again, provide insights, and give actions:
- """,
- }
-
- def __init__(
- self,
- system_prompt: dict[LanguageType, str] | None = None,
- user_prompt: dict[LanguageType, str] | None = None,
- ) -> None:
- """处理Prompt"""
- super().__init__(system_prompt, user_prompt)
-
- async def generate(self, **kwargs) -> str: # noqa: ANN003
- """调用大模型,生成对话总结"""
- last_thought: str = kwargs["last_thought"]
- user_question: str = kwargs["user_question"]
- tool_info: dict[str, Any] = kwargs["tool_info"]
- language: LanguageType = kwargs.get("language", LanguageType.CHINESE)
-
- messages = [
- {"role": "system", "content": "You are a helpful assistant."},
- {
- "role": "user",
- "content": self.user_prompt[language].format(
- last_thought=last_thought,
- user_question=user_question,
- tool_name=tool_info["name"],
- tool_description=tool_info["description"],
- tool_output=tool_info["output"],
- ),
- },
- ]
-
- llm = ReasoningLLM()
- result = ""
- async for chunk in llm.call(messages, streaming=False, temperature=0.7):
- result += chunk
- self.input_tokens = llm.input_tokens
- self.output_tokens = llm.output_tokens
-
- return result
-
-
class ExecutorSummary(CorePattern):
"""使用大模型进行生成Executor初始背景"""
diff --git a/apps/llm/patterns/rewrite.py b/apps/llm/patterns/rewrite.py
deleted file mode 100644
index f4e83230fab8e9dbf8c5d568ed731e297c45f20c..0000000000000000000000000000000000000000
--- a/apps/llm/patterns/rewrite.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved.
-"""问题改写"""
-
-import logging
-from textwrap import dedent
-
-from jinja2 import BaseLoader
-from jinja2.sandbox import SandboxedEnvironment
-from pydantic import BaseModel, Field
-
-from apps.llm.function import JsonGenerator
-from apps.llm.reasoning import ReasoningLLM
-from apps.llm.token import TokenCalculator
-from apps.schemas.enum_var import LanguageType
-
-from .core import CorePattern
-
-logger = logging.getLogger(__name__)
-_env = SandboxedEnvironment(
- loader=BaseLoader,
- autoescape=False,
- trim_blocks=True,
- lstrip_blocks=True,
-)
-
-
-class QuestionRewriteResult(BaseModel):
- """问题补全与重写结果"""
-
- question: str = Field(description="补全后的问题")
-
-
-class QuestionRewrite(CorePattern):
- """问题补全与重写"""
-
- @staticmethod
- def _default() -> tuple[dict[LanguageType, str], dict[LanguageType, str]]:
- """默认的Prompt内容"""
- return {
- LanguageType.CHINESE: r"You are a helpful assistant.",
- LanguageType.ENGLISH: r"You are a helpful assistant.",
- }, {
- LanguageType.CHINESE: dedent(r"""
-
-
- 根据历史对话,推断用户的实际意图并补全用户的提问内容,历史对话被包含在标签中,用户意图被包含在标签中。
- 要求:
- 1. 请使用JSON格式输出,参考下面给出的样例;不要包含任何XML标签,不要包含任何解释说明;
- 2. 若用户当前提问内容与对话上文不相关,或你认为用户的提问内容已足够完整,请直接输出用户的提问内容。
- 3. 补全内容必须精准、恰当,不要编造任何内容。
- 4. 请输出补全后的问题,不要输出其他内容。
- 输出格式样例:
- ```json
- {
- "question": "补全后的问题"
- }
- ```
-
-
-
-
-
-
- openEuler的优势有哪些?
-
-
- openEuler的优势包括开源、社区支持、以及对云计算和边缘计算的优化。
-
-
-
-
-
- 详细点?
-
-
-
-
-
-
- {{history}}
-
-
- {{question}}
-
-
- 现在,请输出补全后的问题:
-