132 lines
4.4 KiB
Rust
132 lines
4.4 KiB
Rust
//! Skill perception system for the AI agent.
|
|
//!
|
|
//! Provides three perception modes for injecting relevant skills into the agent's context:
|
|
//!
|
|
//! - **Auto (自动感知)**: Background awareness that scans conversation content for skill
|
|
//! relevance based on keyword matching and semantic similarity.
|
|
//!
|
|
//! - **Active (主动感知)**: Proactive skill retrieval triggered by explicit user intent,
|
|
//! such as mentioning a skill slug directly in the message. Both keyword and vector-based.
|
|
//!
|
|
//! - **Passive (被动感知)**: Reactive skill retrieval triggered by tool-call events,
|
|
//! such as when the agent mentions a specific skill in its reasoning. Both keyword and
|
|
//! vector-based.
|
|
|
|
pub mod active;
|
|
pub mod auto;
|
|
pub mod passive;
|
|
pub mod vector;
|
|
|
|
pub use active::ActiveSkillAwareness;
|
|
pub use auto::AutoSkillAwareness;
|
|
pub use passive::PassiveSkillAwareness;
|
|
pub use vector::{VectorActiveAwareness, VectorPassiveAwareness};
|
|
|
|
use async_openai::types::chat::ChatCompletionRequestMessage;
|
|
|
|
/// A chunk of skill context ready to be injected into the message list.
|
|
#[derive(Debug, Clone)]
|
|
pub struct SkillContext {
|
|
/// Human-readable label shown to the AI, e.g. "Active skill: code-review"
|
|
pub label: String,
|
|
/// The actual skill content to inject.
|
|
pub content: String,
|
|
}
|
|
|
|
/// Converts skill context into a system message for injection.
|
|
impl SkillContext {
|
|
pub fn to_system_message(self) -> ChatCompletionRequestMessage {
|
|
use async_openai::types::chat::{
|
|
ChatCompletionRequestSystemMessage,
|
|
ChatCompletionRequestSystemMessageContent,
|
|
};
|
|
ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage {
|
|
content: ChatCompletionRequestSystemMessageContent::Text(format!(
|
|
"[{}]\n{}",
|
|
self.label, self.content
|
|
)),
|
|
..Default::default()
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Unified perception service combining all three modes.
|
|
#[derive(Debug, Clone)]
|
|
pub struct PerceptionService {
|
|
pub auto: AutoSkillAwareness,
|
|
pub active: ActiveSkillAwareness,
|
|
pub passive: PassiveSkillAwareness,
|
|
}
|
|
|
|
impl Default for PerceptionService {
|
|
fn default() -> Self {
|
|
Self {
|
|
auto: AutoSkillAwareness::default(),
|
|
active: ActiveSkillAwareness::default(),
|
|
passive: PassiveSkillAwareness::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PerceptionService {
|
|
/// Inject relevant skill context into the message list based on current conversation state.
|
|
///
|
|
/// - **auto**: Scans the current input and conversation history for skill-relevant keywords
|
|
/// and injects matching skills that are enabled.
|
|
/// - **active**: Checks if the user explicitly invoked a skill by slug (e.g. "用 code-review")
|
|
/// and injects it.
|
|
/// - **passive**: Checks if any tool-call events or prior observations mention a skill
|
|
/// slug and injects the matching skill.
|
|
///
|
|
/// Returns a list of system messages to prepend to the conversation.
|
|
pub async fn inject_skills(
|
|
&self,
|
|
input: &str,
|
|
history: &[String],
|
|
tool_calls: &[ToolCallEvent],
|
|
enabled_skills: &[SkillEntry],
|
|
) -> Vec<SkillContext> {
|
|
let mut results = Vec::new();
|
|
|
|
// Active: explicit skill invocation (highest priority)
|
|
if let Some(skill) = self.active.detect(input, enabled_skills) {
|
|
results.push(skill);
|
|
}
|
|
|
|
// Passive: triggered by tool-call events
|
|
for tc in tool_calls {
|
|
if let Some(skill) = self.passive.detect(tc, enabled_skills) {
|
|
if !results.iter().any(|r: &SkillContext| r.label == skill.label) {
|
|
results.push(skill);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Auto: keyword-based relevance matching
|
|
let auto_results = self.auto.detect(input, history, enabled_skills).await;
|
|
for skill in auto_results {
|
|
if !results.iter().any(|r: &SkillContext| r.label == skill.label) {
|
|
results.push(skill);
|
|
}
|
|
}
|
|
|
|
results
|
|
}
|
|
}
|
|
|
|
/// A tool-call event used for passive skill detection.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ToolCallEvent {
|
|
pub tool_name: String,
|
|
pub arguments: String,
|
|
}
|
|
|
|
/// A skill entry from the database, used for matching.
|
|
#[derive(Debug, Clone)]
|
|
pub struct SkillEntry {
|
|
pub slug: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub content: String,
|
|
}
|