Files
KiloStar/kilostar/adapter/model_adapter/agent_factory.py
T
zhaoxi 005ce566a8 feat: Provider model_settings 全链路 + 监管节点工具集 + 重型插件注入 + 前端打磨
- Provider model_settings (Provider+Model 级别参数配置): DB JSONB → API → GSM → AgentFactory.resolve → 三节点 agent.run 注入
- 新增 data/toolset/regulatory_toolset/: 监管节点专属工具(query_workflow_status / query_task_list / send_file)
- send_file 从 interactive_toolset 迁移至 regulatory_toolset,interactive 仅保留 approval
- mcp_helper 合入 GlobalPluginManager dispatch tools
- 前端 Provider 弹窗参数设置区加 JSON 编辑器(model_settings)
- 前端 Plugin 页面新增"重型插件"Tab(HeavyPluginList 占位)
- .gitignore 精简:去除系统默认项,修复 data/ 子目录追踪
- data/toolset/ 与 data/plugin/ 首次纳入版本控制

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 13:10:31 +00:00

145 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Sequence, Any, Dict
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.models.gemini import GeminiModel
from pydantic_ai.providers.openai import OpenAIProvider
from pydantic_ai.providers.anthropic import AnthropicProvider
from pydantic_ai.providers.deepseek import DeepSeekProvider
from pydantic_ai.providers.google import GoogleProvider
from pydantic_ai.toolsets import AbstractToolset
from kilostar.core.global_state_machine.model_provider import Provider
from kilostar.utils.agent_model import ResponseModel, DepsModel
from kilostar.utils.error import ModelNotExistError
class AgentFactory:
"""模型工厂:把内部的 ``Provider`` 元数据翻译成 pydantic-ai 的 ``Agent``。
支持 openai / claude / deepseek / gemini 四类后端,差异通过
``_models_mapping`` 中的 ``model_class`` + ``provider_class`` 键值对屏蔽。
同时支持传入本地工具(tools)和外部工具集(toolsets),包括 MCP 服务器。
"""
def __init__(self):
self._models_mapping = {
"openai": {
"model_class": OpenAIChatModel,
"provider_class": OpenAIProvider,
"provider_kwargs": {"base_url": True, "api_key": True},
},
"claude": {
"model_class": AnthropicModel,
"provider_class": AnthropicProvider,
"provider_kwargs": {"api_key": True},
},
"deepseek": {
"model_class": OpenAIChatModel,
"provider_class": DeepSeekProvider,
"provider_kwargs": {"api_key": True},
},
"gemini": {
"model_class": GeminiModel,
"provider_class": GoogleProvider,
"provider_kwargs": {"api_key": True},
},
}
def create_agent(
self,
provider: Provider,
model_id: str,
output_type: ResponseModel,
system_prompt: str,
deps_type: DepsModel,
agent_name: str,
tools: list = None,
toolsets: Sequence[AbstractToolset[Any]] = None,
) -> Agent:
"""将输入的 provider 对象实例化为一个 pydantic-ai 的 agent 对象。
Args:
provider: Provider 对象,从 global_state_machine 中获取
model_id: 模型名
output_type: 输出格式
system_prompt: 系统提示词
deps_type: 依赖类型,在 agent 运行时动态输入的格式化消息
agent_name: agent 的名字
tools: 本地工具函数列表
toolsets: 外部工具集列表(包括 MCP 服务器等 AbstractToolset 实例)
Returns:
被实例化的 pydantic-ai 的 Agent 对象
"""
if model_id not in provider.provider_models:
raise ModelNotExistError("模型不存在")
if provider.provider_type not in self._models_mapping:
raise ValueError(f"不支持的协议类型: {provider.provider_type}")
config = self._models_mapping[provider.provider_type]
model_class = config["model_class"]
provider_class = config["provider_class"]
provider_kwargs = config["provider_kwargs"]
# 构建 provider 实例化参数
init_kwargs = {}
if provider_kwargs.get("api_key"):
init_kwargs["api_key"] = provider.provider_apikey
if provider_kwargs.get("base_url"):
init_kwargs["base_url"] = provider.provider_url
model_provider = provider_class(**init_kwargs)
# 对于 Geminiprovider 需要传递给 model
if provider.provider_type == "gemini":
model = model_class(
model_name=model_id,
provider=model_provider,
)
else:
model = model_class(model_id, provider=model_provider)
# 创建 Agent,同时传入 tools 和 toolsets
agent = Agent(
model=model,
name=agent_name,
system_prompt=system_prompt,
output_type=output_type,
deps_type=deps_type,
tools=tools or [],
toolsets=toolsets or [],
)
return agent
@staticmethod
def resolve_model_settings(provider: Provider, model_id: str) -> Dict[str, Any]:
"""合并 provider.model_settings 中 ``__default__`` 与具体 model_id 的参数。
- ``__default__`` 是全 Provider 的兜底参数
- ``model_id`` 键覆盖 default 中相同 key
- 都缺省时返回空 dict``agent.run(model_settings={})`` 等效于不传)
"""
settings = getattr(provider, "model_settings", None) or {}
if not isinstance(settings, dict):
return {}
default = settings.get("__default__", {}) or {}
specific = settings.get(model_id, {}) or {}
merged: Dict[str, Any] = {**default, **specific}
return merged