fix(runtime,kernel): HandTool 空壳修复 — 桥接到 HandRegistry 真实执行
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
B-HAND-1 修复: LLM 调用 hand_quiz/hand_researcher 等 Hand 工具后,
HandTool::execute() 原来返回假成功 JSON, 实际 Hand 并不执行.
修复方案 (沿用 SkillExecutor 模式):
- tool.rs: 新增 HandExecutor trait + ToolContext.hand_executor 字段
- hand_tool.rs: execute() 通过 context.hand_executor 分发到真实执行
- loop_runner.rs: AgentLoop 新增 hand_executor 字段 + builder + 3处 ToolContext 传递
- adapters.rs: 新增 KernelHandExecutor 桥接 HandRegistry.execute()
- kernel/mod.rs: 初始化 KernelHandExecutor + 注册到 AgentLoop
- messaging.rs: 两处 AgentLoop 构建添加 .with_hand_executor()
数据流: LLM tool call → HandTool::execute() → ToolContext.hand_executor
→ KernelHandExecutor → HandRegistry.execute() → Hand trait impl
809 tests passed, 0 failed.
This commit is contained in:
@@ -3,11 +3,12 @@
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use zclaw_runtime::{LlmDriver, tool::SkillExecutor};
|
||||
use zclaw_runtime::{LlmDriver, tool::{SkillExecutor, HandExecutor}};
|
||||
use zclaw_skills::{SkillRegistry, LlmCompleter};
|
||||
use zclaw_types::Result;
|
||||
use zclaw_hands::HandRegistry;
|
||||
use zclaw_types::{AgentId, Result};
|
||||
|
||||
/// Adapter that bridges `zclaw_runtime::LlmDriver` -> `zclaw_skills::LlmCompleter`
|
||||
pub(crate) struct LlmDriverAdapter {
|
||||
@@ -134,3 +135,47 @@ impl AgentInbox {
|
||||
self.pending.push_back(envelope);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hand executor implementation for Kernel
|
||||
///
|
||||
/// Bridges `zclaw_runtime::tool::HandExecutor` → `zclaw_hands::HandRegistry`,
|
||||
/// allowing `HandTool::execute()` to dispatch to the real Hand implementations.
|
||||
pub struct KernelHandExecutor {
|
||||
hands: Arc<HandRegistry>,
|
||||
}
|
||||
|
||||
impl KernelHandExecutor {
|
||||
pub fn new(hands: Arc<HandRegistry>) -> Self {
|
||||
Self { hands }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl HandExecutor for KernelHandExecutor {
|
||||
async fn execute_hand(
|
||||
&self,
|
||||
hand_id: &str,
|
||||
agent_id: &AgentId,
|
||||
input: Value,
|
||||
) -> Result<Value> {
|
||||
let context = zclaw_hands::HandContext {
|
||||
agent_id: agent_id.clone(),
|
||||
working_dir: None,
|
||||
env: std::collections::HashMap::new(),
|
||||
timeout_secs: 300,
|
||||
callback_url: None,
|
||||
};
|
||||
let result = self.hands.execute(hand_id, &context, input).await?;
|
||||
if result.success {
|
||||
Ok(result.output)
|
||||
} else {
|
||||
Ok(json!({
|
||||
"hand_id": hand_id,
|
||||
"status": "failed",
|
||||
"error": result.error.unwrap_or_else(|| "Unknown hand execution error".to_string()),
|
||||
"output": result.output,
|
||||
"duration_ms": result.duration_ms,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ impl Kernel {
|
||||
)
|
||||
.with_model(&model)
|
||||
.with_skill_executor(self.skill_executor.clone())
|
||||
.with_hand_executor(self.hand_executor.clone())
|
||||
.with_max_tokens(agent_config.max_tokens.unwrap_or_else(|| self.config.max_tokens()))
|
||||
.with_temperature(agent_config.temperature.unwrap_or_else(|| self.config.temperature()))
|
||||
.with_compaction_threshold(
|
||||
@@ -176,6 +177,7 @@ impl Kernel {
|
||||
)
|
||||
.with_model(&model)
|
||||
.with_skill_executor(self.skill_executor.clone())
|
||||
.with_hand_executor(self.hand_executor.clone())
|
||||
.with_max_tokens(agent_config.max_tokens.unwrap_or_else(|| self.config.max_tokens()))
|
||||
.with_temperature(agent_config.temperature.unwrap_or_else(|| self.config.temperature()))
|
||||
.with_compaction_threshold(
|
||||
|
||||
@@ -27,6 +27,7 @@ use zclaw_skills::SkillRegistry;
|
||||
use zclaw_hands::{HandRegistry, hands::{BrowserHand, QuizHand, ResearcherHand, CollectorHand, ClipHand, TwitterHand, ReminderHand, quiz::LlmQuizGenerator}};
|
||||
|
||||
pub use adapters::KernelSkillExecutor;
|
||||
pub use adapters::KernelHandExecutor;
|
||||
pub use messaging::ChatModeConfig;
|
||||
|
||||
/// The ZCLAW Kernel
|
||||
@@ -40,6 +41,7 @@ pub struct Kernel {
|
||||
llm_completer: Arc<dyn zclaw_skills::LlmCompleter>,
|
||||
skills: Arc<SkillRegistry>,
|
||||
skill_executor: Arc<KernelSkillExecutor>,
|
||||
hand_executor: Arc<KernelHandExecutor>,
|
||||
hands: Arc<HandRegistry>,
|
||||
/// Cached hand configs (populated at boot, used for tool registry)
|
||||
hand_configs: Vec<zclaw_hands::HandConfig>,
|
||||
@@ -105,6 +107,9 @@ impl Kernel {
|
||||
// Create skill executor
|
||||
let skill_executor = Arc::new(KernelSkillExecutor::new(skills.clone(), driver.clone()));
|
||||
|
||||
// Create hand executor — bridges HandTool calls to the HandRegistry
|
||||
let hand_executor = Arc::new(KernelHandExecutor::new(hands.clone()));
|
||||
|
||||
// Create LLM completer for skill system (shared with skill_executor)
|
||||
let llm_completer: Arc<dyn zclaw_skills::LlmCompleter> =
|
||||
Arc::new(adapters::LlmDriverAdapter {
|
||||
@@ -152,6 +157,7 @@ impl Kernel {
|
||||
llm_completer,
|
||||
skills,
|
||||
skill_executor,
|
||||
hand_executor,
|
||||
hands,
|
||||
hand_configs,
|
||||
trigger_manager,
|
||||
|
||||
Reference in New Issue
Block a user