Separate agent tuning from model setup
This commit is contained in:
@@ -9,9 +9,19 @@ from app.core.dependencies import get_current_admin
|
||||
from app.core.responses import api_success
|
||||
from app.models.admin import Admin
|
||||
from app.models.ai_config import ModelConfig, Prompt, SystemConfig
|
||||
from app.schemas.admin import EnableModelRequest, ModelSaveRequest, PromptSaveRequest, SystemConfigSaveRequest
|
||||
from app.models.knowledge import Knowledge
|
||||
from app.schemas.admin import (
|
||||
AgentDebugRequest,
|
||||
EnableModelRequest,
|
||||
ModelSaveRequest,
|
||||
PromptSaveRequest,
|
||||
SystemConfigSaveRequest,
|
||||
)
|
||||
from app.services.admin_service import OperationLogService
|
||||
from app.services.feishu_service import FeishuKnowledgeService
|
||||
from app.services.knowledge_service import KnowledgeScope
|
||||
from app.services.model_service import ModelClientService
|
||||
from app.services.rag_service import RagResult
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -48,6 +58,37 @@ def reset_prompt(db: Session = Depends(get_db), current_admin: Admin = Depends(g
|
||||
return api_success({"promptContent": prompt.prompt_content})
|
||||
|
||||
|
||||
@router.post("/agent/debug")
|
||||
def debug_agent(
|
||||
payload: AgentDebugRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: Admin = Depends(get_current_admin),
|
||||
) -> dict:
|
||||
model = db.get(ModelConfig, payload.modelId)
|
||||
if model is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")
|
||||
|
||||
scopes = _agent_knowledge_scopes(db, payload.knowledgeIds)
|
||||
chunks = FeishuKnowledgeService.retrieve(payload.question, scopes)
|
||||
prompt = _build_agent_debug_prompt(payload.promptContent, payload.question, chunks)
|
||||
rag_result = RagResult(question=payload.question, knowledge_scopes=scopes, chunks=chunks, prompt=prompt)
|
||||
result = ModelClientService.debug_model(
|
||||
model,
|
||||
rag_result,
|
||||
{
|
||||
"temperature": payload.temperature,
|
||||
"top_p": payload.topP,
|
||||
"top_k": payload.topK,
|
||||
"presence_penalty": payload.presencePenalty,
|
||||
"frequency_penalty": payload.frequencyPenalty,
|
||||
"max_token": payload.maxToken,
|
||||
},
|
||||
)
|
||||
OperationLogService.write(db, admin_id=current_admin.id, module="agent", action="debug", target_id=model.id)
|
||||
db.commit()
|
||||
return api_success(result)
|
||||
|
||||
|
||||
@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()
|
||||
@@ -235,6 +276,32 @@ def _model_dict(model: ModelConfig) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _agent_knowledge_scopes(db: Session, knowledge_ids: list[int]) -> list[KnowledgeScope]:
|
||||
query = select(Knowledge).where(Knowledge.status == 1)
|
||||
if knowledge_ids:
|
||||
query = query.where(Knowledge.id.in_(knowledge_ids))
|
||||
rows = db.scalars(query.order_by(Knowledge.id.asc())).all()
|
||||
return [
|
||||
KnowledgeScope(
|
||||
id=item.id,
|
||||
name=item.name,
|
||||
feishu_space_id=item.feishu_space_id,
|
||||
feishu_node_id=item.feishu_node_id,
|
||||
)
|
||||
for item in rows
|
||||
]
|
||||
|
||||
|
||||
def _build_agent_debug_prompt(prompt_content: str, question: str, chunks: list) -> str:
|
||||
context = "\n\n".join(
|
||||
f"[知识片段 {index}] 来源:{chunk.knowledge_name} / {chunk.title}\n{chunk.content}"
|
||||
for index, chunk in enumerate(chunks, start=1)
|
||||
)
|
||||
if not context:
|
||||
context = "未检索到相关知识片段。"
|
||||
return f"{prompt_content.strip()}\n\n{context}\n\n用户问题:{question.strip()}"
|
||||
|
||||
|
||||
def _config_dict(config: SystemConfig) -> dict:
|
||||
return {
|
||||
"id": config.id,
|
||||
|
||||
@@ -51,6 +51,19 @@ class PromptSaveRequest(BaseModel):
|
||||
promptContent: str = Field(min_length=1)
|
||||
|
||||
|
||||
class AgentDebugRequest(BaseModel):
|
||||
promptContent: str = Field(min_length=1)
|
||||
modelId: int = Field(gt=0)
|
||||
knowledgeIds: list[int] = Field(default_factory=list)
|
||||
question: str = Field(min_length=1, max_length=2000)
|
||||
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 | None = Field(default=1024, ge=1, le=100000)
|
||||
|
||||
|
||||
class ModelSaveRequest(BaseModel):
|
||||
provider: str = Field(min_length=1, max_length=50)
|
||||
displayName: str | None = Field(default=None, max_length=100)
|
||||
|
||||
@@ -59,6 +59,23 @@ class ModelClientService:
|
||||
return {"ok": False, "message": str(exc), "answer": ""}
|
||||
return {"ok": True, "message": "模型配置可用", "answer": answer[:500]}
|
||||
|
||||
@staticmethod
|
||||
def debug_model(model: ModelConfig, rag_result: RagResult, overrides: dict[str, Any]) -> dict[str, Any]:
|
||||
debug_model = _copy_model_with_overrides(model, overrides)
|
||||
try:
|
||||
answer = _call_configured_model(debug_model, rag_result, allow_no_hit=True)
|
||||
except ExternalServiceError as exc:
|
||||
return {"ok": False, "message": str(exc), "answer": "", "prompt": rag_result.prompt}
|
||||
return {
|
||||
"ok": True,
|
||||
"message": "Agent 调试完成",
|
||||
"answer": answer[:4000],
|
||||
"prompt": rag_result.prompt,
|
||||
"modelName": model.model_name,
|
||||
"retrieveCount": len(rag_result.chunks),
|
||||
"knowledgeIds": rag_result.knowledge_ids,
|
||||
}
|
||||
|
||||
|
||||
def _mock_answer(rag_result: RagResult) -> str:
|
||||
if not rag_result.is_hit:
|
||||
@@ -75,6 +92,35 @@ def _mock_answer(rag_result: RagResult) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _copy_model_with_overrides(model: ModelConfig, overrides: dict[str, Any]) -> ModelConfig:
|
||||
debug_model = ModelConfig(
|
||||
provider=model.provider,
|
||||
display_name=model.display_name,
|
||||
api_type=model.api_type,
|
||||
model_name=model.model_name,
|
||||
base_url=model.base_url,
|
||||
api_url=model.api_url,
|
||||
api_key=model.api_key,
|
||||
auth_type=model.auth_type,
|
||||
api_version=model.api_version,
|
||||
temperature=overrides.get("temperature"),
|
||||
top_p=overrides.get("top_p"),
|
||||
top_k=overrides.get("top_k"),
|
||||
presence_penalty=overrides.get("presence_penalty"),
|
||||
frequency_penalty=overrides.get("frequency_penalty"),
|
||||
max_token=overrides.get("max_token"),
|
||||
context_window=model.context_window,
|
||||
stream_enabled=model.stream_enabled,
|
||||
response_format=model.response_format,
|
||||
extra_params=model.extra_params,
|
||||
remark=model.remark,
|
||||
timeout_second=model.timeout_second,
|
||||
enabled=model.enabled,
|
||||
)
|
||||
debug_model.id = model.id
|
||||
return debug_model
|
||||
|
||||
|
||||
def _rough_token_count(text: str) -> int:
|
||||
return max(1, len(text.strip()) // 2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user