Agent 管理
-
维护主提示词及历史版本,配置本次调试参数,并验证真实检索与回答效果。
+
维护主提示词、用户端正式生成参数和历史版本,并验证真实检索与回答效果。
当前版本 {{ prompt?.id ? `#${prompt.id}` : "系统默认" }}
@@ -286,11 +349,34 @@ function errorMessage(error: unknown, fallback: string) {
+
+
+
用户端正式运行参数
保存后从用户端下一条消息开始生效。当前正式模型:{{ runtimeConfig?.modelName || '未启用模型' }}
+
保存正式参数
+
+
+
+
+
+
+
+
本次调试配置
配置只作用于后台预览,不会修改模型和知识库的正式状态。
-
+
@@ -301,32 +387,14 @@ function errorMessage(error: unknown, fallback: string) {
高级生成参数
仅影响本次后台调试
-
-
- 回答随机性
-
-
-
- 核采样概率
-
-
-
- 候选词数量
-
-
-
- 最大输出长度
-
-
-
- 主题重复惩罚
-
-
-
- 高频重复惩罚
-
-
-
+
diff --git a/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts b/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts
index 9b55f65..1276415 100644
--- a/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts
+++ b/ai_knowledge_base_v2/apps/admin-web/src/services/api.ts
@@ -2,6 +2,8 @@ import type {
AdminProfile,
AdminUser,
AgentDebugResult,
+ AgentGenerationConfig,
+ AgentRuntimeConfig,
AiLogRecord,
ApiResponse,
ChatDetail,
@@ -178,6 +180,9 @@ export const api = {
restorePrompt: (id: number) => request
(`/admin/prompt/history/${id}/restore`, { method: "POST", body: "{}" }),
debugAgent: (payload: Record) =>
request("/admin/agent/debug", { method: "POST", body: JSON.stringify(payload) }),
+ agentRuntimeConfig: () => request("/admin/agent/runtime-config"),
+ saveAgentRuntimeConfig: (payload: AgentGenerationConfig) =>
+ request("/admin/agent/runtime-config", { method: "PUT", body: JSON.stringify(payload) }),
models: () => request("/admin/model/list"),
createModel: (payload: Record) =>
request("/admin/model", { method: "POST", body: JSON.stringify(payload) }),
diff --git a/ai_knowledge_base_v2/apps/admin-web/src/types/api.ts b/ai_knowledge_base_v2/apps/admin-web/src/types/api.ts
index d922ef3..16b0571 100644
--- a/ai_knowledge_base_v2/apps/admin-web/src/types/api.ts
+++ b/ai_knowledge_base_v2/apps/admin-web/src/types/api.ts
@@ -133,6 +133,20 @@ export interface ModelItem {
enabled: number;
}
+export interface AgentGenerationConfig {
+ temperature: number | null;
+ topP: number | null;
+ topK: number | null;
+ presencePenalty: number | null;
+ frequencyPenalty: number | null;
+ maxToken: number;
+}
+
+export interface AgentRuntimeConfig extends AgentGenerationConfig {
+ modelId: number | null;
+ modelName: string | null;
+}
+
export interface AgentDebugResult {
ok: boolean;
message: string;
diff --git a/ai_knowledge_base_v2/apps/backend/app/api/admin_settings.py b/ai_knowledge_base_v2/apps/backend/app/api/admin_settings.py
index 85f6ead..d024a1d 100644
--- a/ai_knowledge_base_v2/apps/backend/app/api/admin_settings.py
+++ b/ai_knowledge_base_v2/apps/backend/app/api/admin_settings.py
@@ -13,6 +13,7 @@ from app.models.ai_config import ModelConfig, Prompt, SystemConfig
from app.models.knowledge import Knowledge
from app.schemas.admin import (
AgentDebugRequest,
+ AgentRuntimeConfigSaveRequest,
EnableModelRequest,
ModelSaveRequest,
PromptSaveRequest,
@@ -184,6 +185,46 @@ async def debug_agent(
return api_success(result)
+@router.get("/agent/runtime-config")
+def get_agent_runtime_config(
+ db: Session = Depends(get_db),
+ current_admin: Admin = Depends(get_current_admin),
+) -> dict:
+ model = _enabled_model(db)
+ return api_success(_agent_runtime_config_dict(model))
+
+
+@router.put("/agent/runtime-config")
+def save_agent_runtime_config(
+ payload: AgentRuntimeConfigSaveRequest,
+ db: Session = Depends(get_db),
+ current_admin: Admin = Depends(get_current_admin),
+) -> dict:
+ model = _enabled_model(db)
+ if model is None:
+ raise HTTPException(
+ status_code=status.HTTP_409_CONFLICT,
+ detail="当前没有已启用模型,请先在模型管理中启用一个模型",
+ )
+ model.temperature = payload.temperature
+ model.top_p = payload.topP
+ model.top_k = payload.topK
+ model.presence_penalty = payload.presencePenalty
+ model.frequency_penalty = payload.frequencyPenalty
+ model.max_token = payload.maxToken
+ db.add(model)
+ OperationLogService.write(
+ db,
+ admin_id=current_admin.id,
+ module="agent",
+ action="save_runtime_config",
+ target_id=model.id,
+ )
+ db.commit()
+ db.refresh(model)
+ return api_success(_agent_runtime_config_dict(model))
+
+
@router.get("/model/list")
def list_models(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
models = db.scalars(select(ModelConfig).order_by(ModelConfig.id.desc())).all()
@@ -376,6 +417,28 @@ def _model_dict(model: ModelConfig) -> dict:
}
+def _enabled_model(db: Session) -> ModelConfig | None:
+ return db.scalar(
+ select(ModelConfig)
+ .where(ModelConfig.enabled == 1)
+ .order_by(ModelConfig.id.desc())
+ .limit(1)
+ )
+
+
+def _agent_runtime_config_dict(model: ModelConfig | None) -> dict:
+ return {
+ "modelId": model.id if model is not None else None,
+ "modelName": (model.display_name or model.model_name) if model is not None else None,
+ "temperature": float(model.temperature) if model is not None and model.temperature is not None else None,
+ "topP": float(model.top_p) if model is not None and model.top_p is not None else None,
+ "topK": model.top_k if model is not None else None,
+ "presencePenalty": float(model.presence_penalty) if model is not None and model.presence_penalty is not None else None,
+ "frequencyPenalty": float(model.frequency_penalty) if model is not None and model.frequency_penalty is not None else None,
+ "maxToken": model.max_token if model is not None and model.max_token is not None else 8192,
+ }
+
+
def _default_prompt_detail() -> dict:
return {
"id": None,
diff --git a/ai_knowledge_base_v2/apps/backend/app/schemas/admin.py b/ai_knowledge_base_v2/apps/backend/app/schemas/admin.py
index 331078a..a7077eb 100644
--- a/ai_knowledge_base_v2/apps/backend/app/schemas/admin.py
+++ b/ai_knowledge_base_v2/apps/backend/app/schemas/admin.py
@@ -89,7 +89,16 @@ class AgentDebugRequest(BaseModel):
topK: int | None = Field(default=None, ge=1, le=1000)
presencePenalty: float | None = Field(default=None, ge=-2, le=2)
frequencyPenalty: float | None = Field(default=None, ge=-2, le=2)
- maxToken: int | None = Field(default=1024, ge=1, le=100000)
+ maxToken: int | None = Field(default=8192, ge=1, le=100000)
+
+
+class AgentRuntimeConfigSaveRequest(BaseModel):
+ temperature: float | None = Field(default=None, ge=0, le=2)
+ topP: float | None = Field(default=None, ge=0, le=1)
+ topK: int | None = Field(default=None, ge=1, le=1000)
+ presencePenalty: float | None = Field(default=None, ge=-2, le=2)
+ frequencyPenalty: float | None = Field(default=None, ge=-2, le=2)
+ maxToken: int = Field(default=8192, ge=256, le=100000)
class ModelSaveRequest(BaseModel):
diff --git a/ai_knowledge_base_v2/apps/backend/app/services/model_service.py b/ai_knowledge_base_v2/apps/backend/app/services/model_service.py
index 69fdffb..0049b3a 100644
--- a/ai_knowledge_base_v2/apps/backend/app/services/model_service.py
+++ b/ai_knowledge_base_v2/apps/backend/app/services/model_service.py
@@ -494,7 +494,7 @@ def _agent_system_prompt() -> str:
def _max_output_tokens(model: ModelConfig) -> int:
- configured = max(1, model.max_token or 1024)
+ configured = max(1, model.max_token or 8192)
if not model.context_window:
return configured
return min(configured, max(1, model.context_window // 3))
diff --git a/ai_knowledge_base_v2/apps/backend/tests/test_agent_runtime_config.py b/ai_knowledge_base_v2/apps/backend/tests/test_agent_runtime_config.py
new file mode 100644
index 0000000..d77fe7a
--- /dev/null
+++ b/ai_knowledge_base_v2/apps/backend/tests/test_agent_runtime_config.py
@@ -0,0 +1,101 @@
+from decimal import Decimal
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import Session
+from sqlalchemy.pool import StaticPool
+
+from app.api.admin_settings import get_agent_runtime_config, save_agent_runtime_config
+from app.models import Base
+from app.models.admin import Admin
+from app.models.ai_config import ModelConfig
+from app.schemas.admin import AgentRuntimeConfigSaveRequest
+from app.services.model_stream_service import _openai_stream_payload
+from app.services.model_service import _max_output_tokens
+from app.services.rag_service import RagResult
+
+
+def _database() -> Session:
+ engine = create_engine(
+ "sqlite:///:memory:",
+ connect_args={"check_same_thread": False},
+ poolclass=StaticPool,
+ )
+ Base.metadata.create_all(engine)
+ return Session(engine)
+
+
+def _admin() -> Admin:
+ return Admin(id=1, username="admin", password="hash", name="系统管理员", status=1)
+
+
+def _model() -> ModelConfig:
+ return ModelConfig(
+ id=1,
+ provider="openai-compatible",
+ display_name="正式模型",
+ api_type="openai_compatible",
+ model_name="production-model",
+ base_url="https://example.com/v1",
+ api_url="",
+ api_key="encrypted",
+ auth_type="bearer",
+ max_token=None,
+ stream_enabled=1,
+ timeout_second=30,
+ enabled=1,
+ )
+
+
+def test_runtime_config_defaults_to_long_answer_safe_max_tokens():
+ with _database() as db:
+ admin = _admin()
+ model = _model()
+ db.add_all([admin, model])
+ db.commit()
+
+ result = get_agent_runtime_config(db=db, current_admin=admin)["data"]
+
+ assert result["modelId"] == model.id
+ assert result["modelName"] == "正式模型"
+ assert result["maxToken"] == 8192
+ assert _max_output_tokens(model) == 8192
+
+
+def test_saved_runtime_config_is_persisted_on_enabled_model():
+ with _database() as db:
+ admin = _admin()
+ model = _model()
+ db.add_all([admin, model])
+ db.commit()
+
+ result = save_agent_runtime_config(
+ AgentRuntimeConfigSaveRequest(
+ temperature=0.3,
+ topP=0.9,
+ topK=40,
+ presencePenalty=0.2,
+ frequencyPenalty=0.4,
+ maxToken=12000,
+ ),
+ db=db,
+ current_admin=admin,
+ )["data"]
+
+ db.refresh(model)
+ assert result["maxToken"] == 12000
+ assert model.max_token == 12000
+ assert model.temperature == Decimal("0.30")
+ assert model.top_p == Decimal("0.90")
+ assert model.top_k == 40
+ assert model.presence_penalty == Decimal("0.20")
+ assert model.frequency_penalty == Decimal("0.40")
+
+ payload = _openai_stream_payload(
+ model,
+ RagResult(question="测试", knowledge_scopes=[], chunks=[], prompt="测试", allow_general_knowledge=True),
+ )
+ assert payload["max_tokens"] == 12000
+ assert payload["temperature"] == 0.3
+ assert payload["top_p"] == 0.9
+ assert payload["presence_penalty"] == 0.2
+ assert payload["frequency_penalty"] == 0.4