наблюдаемость: измерять бюджет каталога MCP
This commit is contained in:
@@ -10,6 +10,7 @@ pub mod observability;
|
||||
pub mod operation;
|
||||
pub mod protocol;
|
||||
pub mod secret;
|
||||
pub mod tool_catalog;
|
||||
pub mod tool_quality;
|
||||
pub mod workspace;
|
||||
|
||||
@@ -50,6 +51,7 @@ pub mod domain {
|
||||
};
|
||||
pub use crate::protocol::{AuthKind, ExportMode, HttpMethod, Protocol};
|
||||
pub use crate::secret::{Secret, SecretKind, SecretStatus, SecretVersion};
|
||||
pub use crate::tool_catalog::{ToolCatalogAnalysis, ToolCatalogBudget};
|
||||
pub use crate::tool_quality::{
|
||||
ToolQualityCatalogTool, ToolQualityFinding, ToolQualityMappingRule, ToolQualityMappingSet,
|
||||
ToolQualityReport, ToolQualitySchemaKind, ToolQualitySchemaNode, ToolQualitySeverity,
|
||||
@@ -141,6 +143,10 @@ pub use operation::{
|
||||
};
|
||||
pub use protocol::{AuthKind, ExportMode, HttpMethod, Protocol};
|
||||
pub use secret::{Secret, SecretKind, SecretStatus, SecretVersion};
|
||||
pub use tool_catalog::{
|
||||
RECOMMENDED_TOOL_CATALOG_CONTEXT_TOKENS, ToolCatalogAnalysis, ToolCatalogAnalysisError,
|
||||
ToolCatalogBudget, analyze_tool_catalog,
|
||||
};
|
||||
pub use tool_quality::{
|
||||
ToolQualityCatalogTool, ToolQualityFinding, ToolQualityMappingRule, ToolQualityMappingSet,
|
||||
ToolQualityReport, ToolQualitySchemaKind, ToolQualitySchemaNode, ToolQualitySeverity,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::tool_quality::{
|
||||
ToolQualityCatalogTool, ToolQualityFinding, ToolQualityReport, ToolQualitySeverity,
|
||||
analyze_agent_tool_catalog_quality,
|
||||
};
|
||||
|
||||
pub const RECOMMENDED_TOOL_CATALOG_CONTEXT_TOKENS: usize = 4_096;
|
||||
const ESTIMATED_TOKEN_UTF8_BYTES: usize = 3;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ToolCatalogBudget {
|
||||
pub tool_count: usize,
|
||||
pub serialized_bytes: usize,
|
||||
pub estimated_context_tokens: usize,
|
||||
pub largest_tool_estimated_context_tokens: usize,
|
||||
pub recommended_context_tokens: usize,
|
||||
pub exceeds_recommended_budget: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ToolCatalogAnalysis {
|
||||
pub budget: ToolCatalogBudget,
|
||||
pub quality: ToolQualityReport,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ToolCatalogAnalysisError {
|
||||
#[error("tool catalog and definitions length mismatch")]
|
||||
DefinitionCountMismatch,
|
||||
#[error("tool catalog definition is missing string field: {0}")]
|
||||
DefinitionFieldMissing(&'static str),
|
||||
#[error("tool catalog definition serialization failed: {0}")]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
pub fn analyze_tool_catalog(
|
||||
tools: &[ToolQualityCatalogTool],
|
||||
definitions: &[Value],
|
||||
) -> Result<ToolCatalogAnalysis, ToolCatalogAnalysisError> {
|
||||
if tools.len() != definitions.len() {
|
||||
return Err(ToolCatalogAnalysisError::DefinitionCountMismatch);
|
||||
}
|
||||
|
||||
let mut serialized_bytes = 0usize;
|
||||
let mut estimated_context_tokens = 0usize;
|
||||
let mut largest_tool_estimated_context_tokens = 0usize;
|
||||
for definition in definitions {
|
||||
let definition_bytes = serde_json::to_vec(definition)?.len();
|
||||
let definition_tokens = estimate_context_tokens(definition_bytes);
|
||||
serialized_bytes = serialized_bytes.saturating_add(definition_bytes);
|
||||
estimated_context_tokens = estimated_context_tokens.saturating_add(definition_tokens);
|
||||
largest_tool_estimated_context_tokens =
|
||||
largest_tool_estimated_context_tokens.max(definition_tokens);
|
||||
}
|
||||
|
||||
let exceeds_recommended_budget =
|
||||
estimated_context_tokens > RECOMMENDED_TOOL_CATALOG_CONTEXT_TOKENS;
|
||||
let budget = ToolCatalogBudget {
|
||||
tool_count: tools.len(),
|
||||
serialized_bytes,
|
||||
estimated_context_tokens,
|
||||
largest_tool_estimated_context_tokens,
|
||||
recommended_context_tokens: RECOMMENDED_TOOL_CATALOG_CONTEXT_TOKENS,
|
||||
exceeds_recommended_budget,
|
||||
};
|
||||
let mut quality = analyze_agent_tool_catalog_quality(tools);
|
||||
if exceeds_recommended_budget {
|
||||
quality.findings.push(ToolQualityFinding {
|
||||
severity: ToolQualitySeverity::Warning,
|
||||
code: "agent_catalog_context_budget_high".to_owned(),
|
||||
message: "Каталог инструментов занимает слишком много контекста модели.".to_owned(),
|
||||
suggested_action: Some(
|
||||
"Сократите описания и схемы либо разделите инструменты между специализированными агентами."
|
||||
.to_owned(),
|
||||
),
|
||||
field_path: Some("agent.operations".to_owned()),
|
||||
});
|
||||
quality = ToolQualityReport::new(quality.findings);
|
||||
}
|
||||
|
||||
Ok(ToolCatalogAnalysis { budget, quality })
|
||||
}
|
||||
|
||||
fn estimate_context_tokens(serialized_bytes: usize) -> usize {
|
||||
serialized_bytes.div_ceil(ESTIMATED_TOKEN_UTF8_BYTES)
|
||||
}
|
||||
Reference in New Issue
Block a user