sgclaw: snapshot today's runtime and skill updates
This commit is contained in:
@@ -24,6 +24,7 @@ const BROWSER_TOOL_CONTRACT_PROMPT: &str = "SuperRPA browser interface contract:
|
||||
const ZHIHU_HOTLIST_EXECUTION_PROMPT: &str = "Zhihu hotlist execution contract:\n- Treat Zhihu hotlist export/presentation requests as a real browser workflow, not as a text-only summarization task.\n- You must attempt the browser workflow before concluding failure; a prose-only answer is invalid for this workflow.\n- If the current page is not already `https://www.zhihu.com/hot`, navigate there first.\n- If the `zhihu-hotlist.extract_hotlist` skill tool is available, call it before any generic browser probing.\n- Use generic `getText` only as a last-resort fallback when the packaged extractor fails.\n- Extract ordered rows containing `rank`, `title`, and `heat` as structured data.\n- Do not use shell, web_fetch, web_search_tool, or fabricated sample data for this workflow.\n- Do not repeat the same sentence or section in your final answer.";
|
||||
const OFFICE_EXPORT_COMPLETION_PROMPT: &str = "Export completion contract:\n- This task requires a real Excel export.\n- After the Zhihu rows are available, you must call openxml_office before finishing.\n- Never fabricate, simulate, or invent substitute hotlist data when a live collection/export task fails.\n- If live collection fails, report the failure concisely instead of producing fake rows.\n- Do not stop after describing how you will parse or export the data.\n- Do not repeat the same sentence or section in your final answer.\n- Your final answer must include the generated local .xlsx path.";
|
||||
const SCREEN_EXPORT_COMPLETION_PROMPT: &str = "Presentation completion contract:\n- This task requires a real dashboard artifact.\n- After the Zhihu rows are available, you must call screen_html_export before finishing.\n- Do not stop after describing how you will render or present the data.\n- Do not repeat the same sentence or section in your final answer.\n- Your final answer must include the local .html path and the presentation object.";
|
||||
const ZHIHU_WRITE_PUBLISH_PROMPT: &str = "Zhihu article publish contract:\n- This task may publish a Zhihu article.\n- You must not click publish without explicit human confirmation in the current session.\n- If the user asked to publish but no explicit confirmation phrase is present yet, ask for confirmation concisely and stop after the confirmation request.\n- Do not keep exploring tools after you have determined that publish confirmation is missing.\n- If the user only asked to write or draft, stay in draft mode and do not treat it as publish mode.\n- Do not repeat the same sentence or section in your final answer.";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RuntimeEngine {
|
||||
@@ -51,9 +52,7 @@ impl RuntimeEngine {
|
||||
self.tool_policy
|
||||
.allowed_tools
|
||||
.iter()
|
||||
.any(|tool| {
|
||||
tool == BROWSER_ACTION_TOOL_NAME || tool == SUPERRPA_BROWSER_TOOL_NAME
|
||||
})
|
||||
.any(|tool| tool == BROWSER_ACTION_TOOL_NAME || tool == SUPERRPA_BROWSER_TOOL_NAME)
|
||||
}
|
||||
|
||||
pub fn build_agent(
|
||||
@@ -155,6 +154,9 @@ impl RuntimeEngine {
|
||||
if task_needs_screen_export(trimmed_instruction) {
|
||||
sections.push(SCREEN_EXPORT_COMPLETION_PROMPT.to_string());
|
||||
}
|
||||
if task_requests_zhihu_article_publish(trimmed_instruction, page_url, page_title) {
|
||||
sections.push(ZHIHU_WRITE_PUBLISH_PROMPT.to_string());
|
||||
}
|
||||
if let Some(page_context) = build_page_context_message(page_url, page_title) {
|
||||
sections.push(page_context);
|
||||
}
|
||||
@@ -173,17 +175,11 @@ impl RuntimeEngine {
|
||||
.cmp(&right.name)
|
||||
.then(left.version.cmp(&right.version))
|
||||
});
|
||||
skills.dedup_by(|left, right| {
|
||||
left.name == right.name && left.version == right.version
|
||||
});
|
||||
skills.dedup_by(|left, right| left.name == right.name && left.version == right.version);
|
||||
skills
|
||||
}
|
||||
|
||||
pub fn loaded_skill_names(
|
||||
&self,
|
||||
config: &ZeroClawConfig,
|
||||
skills_dir: &Path,
|
||||
) -> Vec<String> {
|
||||
pub fn loaded_skill_names(&self, config: &ZeroClawConfig, skills_dir: &Path) -> Vec<String> {
|
||||
let mut names = self
|
||||
.loaded_skills(config, skills_dir)
|
||||
.into_iter()
|
||||
@@ -237,8 +233,8 @@ impl RuntimeEngine {
|
||||
}
|
||||
allowed_tools.dedup();
|
||||
|
||||
if matches!(self.profile, RuntimeProfile::GeneralAssistant) &&
|
||||
self.tool_policy.may_use_non_browser_tools
|
||||
if matches!(self.profile, RuntimeProfile::GeneralAssistant)
|
||||
&& self.tool_policy.may_use_non_browser_tools
|
||||
{
|
||||
None
|
||||
} else {
|
||||
@@ -263,9 +259,7 @@ fn browser_script_tool_names(skills: &[zeroclaw::skills::Skill]) -> Vec<String>
|
||||
|
||||
fn task_needs_local_file_read(instruction: &str) -> bool {
|
||||
let normalized = instruction.trim();
|
||||
normalized.contains("/home/") ||
|
||||
normalized.contains("./") ||
|
||||
normalized.contains("../")
|
||||
normalized.contains("/home/") || normalized.contains("./") || normalized.contains("../")
|
||||
}
|
||||
|
||||
pub fn is_zhihu_hotlist_task(
|
||||
@@ -277,16 +271,16 @@ pub fn is_zhihu_hotlist_task(
|
||||
let normalized_url = page_url.unwrap_or_default().to_ascii_lowercase();
|
||||
let normalized_title = page_title.unwrap_or_default().to_ascii_lowercase();
|
||||
|
||||
let is_zhihu = normalized_instruction.contains("zhihu") ||
|
||||
instruction.contains("知乎") ||
|
||||
normalized_url.contains("zhihu.com") ||
|
||||
normalized_title.contains("zhihu") ||
|
||||
page_title.unwrap_or_default().contains("知乎");
|
||||
let is_hotlist = normalized_instruction.contains("hotlist") ||
|
||||
instruction.contains("热榜") ||
|
||||
normalized_url.contains("/hot") ||
|
||||
normalized_title.contains("hotlist") ||
|
||||
page_title.unwrap_or_default().contains("热榜");
|
||||
let is_zhihu = normalized_instruction.contains("zhihu")
|
||||
|| instruction.contains("知乎")
|
||||
|| normalized_url.contains("zhihu.com")
|
||||
|| normalized_title.contains("zhihu")
|
||||
|| page_title.unwrap_or_default().contains("知乎");
|
||||
let is_hotlist = normalized_instruction.contains("hotlist")
|
||||
|| instruction.contains("热榜")
|
||||
|| normalized_url.contains("/hot")
|
||||
|| normalized_title.contains("hotlist")
|
||||
|| page_title.unwrap_or_default().contains("热榜");
|
||||
|
||||
is_zhihu && is_hotlist
|
||||
}
|
||||
@@ -310,6 +304,48 @@ fn task_needs_screen_export(instruction: &str) -> bool {
|
||||
|| normalized.contains("汇报")
|
||||
}
|
||||
|
||||
pub fn task_requests_zhihu_article_publish(
|
||||
instruction: &str,
|
||||
page_url: Option<&str>,
|
||||
page_title: Option<&str>,
|
||||
) -> bool {
|
||||
if !is_zhihu_write_task(instruction, page_url, page_title) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let normalized = instruction.to_ascii_lowercase();
|
||||
normalized.contains("publish") || instruction.contains("发布") || instruction.contains("发表")
|
||||
}
|
||||
|
||||
pub fn is_zhihu_write_task(
|
||||
instruction: &str,
|
||||
page_url: Option<&str>,
|
||||
page_title: Option<&str>,
|
||||
) -> bool {
|
||||
let normalized_instruction = instruction.to_ascii_lowercase();
|
||||
let normalized_url = page_url.unwrap_or_default().to_ascii_lowercase();
|
||||
let normalized_title = page_title.unwrap_or_default().to_ascii_lowercase();
|
||||
|
||||
let is_zhihu = normalized_instruction.contains("zhihu")
|
||||
|| instruction.contains("知乎")
|
||||
|| normalized_url.contains("zhihu.com")
|
||||
|| normalized_title.contains("zhihu")
|
||||
|| page_title.unwrap_or_default().contains("知乎");
|
||||
let is_write = normalized_instruction.contains("article")
|
||||
|| normalized_instruction.contains("write")
|
||||
|| normalized_instruction.contains("publish")
|
||||
|| instruction.contains("文章")
|
||||
|| instruction.contains("写")
|
||||
|| instruction.contains("发布")
|
||||
|| instruction.contains("发表")
|
||||
|| normalized_url.contains("creator")
|
||||
|| normalized_url.contains("write")
|
||||
|| page_title.unwrap_or_default().contains("创作")
|
||||
|| page_title.unwrap_or_default().contains("写文章");
|
||||
|
||||
is_zhihu && is_write
|
||||
}
|
||||
|
||||
fn load_runtime_skills(config: &ZeroClawConfig, skills_dir: &Path) -> Vec<zeroclaw::skills::Skill> {
|
||||
let default_skills_dir = config.workspace_dir.join("skills");
|
||||
if skills_dir == default_skills_dir {
|
||||
@@ -344,10 +380,7 @@ fn build_page_context_message(page_url: Option<&str>, page_title: Option<&str>)
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(format!(
|
||||
"Current browser context:\n{}",
|
||||
parts.join("\n")
|
||||
))
|
||||
Some(format!("Current browser context:\n{}", parts.join("\n")))
|
||||
}
|
||||
|
||||
fn map_anyhow_to_pipe_error(err: anyhow::Error) -> PipeError {
|
||||
|
||||
@@ -2,6 +2,8 @@ mod engine;
|
||||
mod profile;
|
||||
mod tool_policy;
|
||||
|
||||
pub use engine::{is_zhihu_hotlist_task, RuntimeEngine};
|
||||
pub use engine::{
|
||||
is_zhihu_hotlist_task, is_zhihu_write_task, task_requests_zhihu_article_publish, RuntimeEngine,
|
||||
};
|
||||
pub use profile::RuntimeProfile;
|
||||
pub use tool_policy::ToolPolicy;
|
||||
|
||||
6
src/runtime/profile.rs
Normal file
6
src/runtime/profile.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RuntimeProfile {
|
||||
BrowserAttached,
|
||||
BrowserHeavy,
|
||||
GeneralAssistant,
|
||||
}
|
||||
@@ -13,18 +13,12 @@ impl ToolPolicy {
|
||||
RuntimeProfile::BrowserAttached => Self {
|
||||
requires_browser_surface: false,
|
||||
may_use_non_browser_tools: true,
|
||||
allowed_tools: vec![
|
||||
"superrpa_browser".to_string(),
|
||||
"browser_action".to_string(),
|
||||
],
|
||||
allowed_tools: vec!["superrpa_browser".to_string(), "browser_action".to_string()],
|
||||
},
|
||||
RuntimeProfile::BrowserHeavy => Self {
|
||||
requires_browser_surface: true,
|
||||
may_use_non_browser_tools: true,
|
||||
allowed_tools: vec![
|
||||
"superrpa_browser".to_string(),
|
||||
"browser_action".to_string(),
|
||||
],
|
||||
allowed_tools: vec!["superrpa_browser".to_string(), "browser_action".to_string()],
|
||||
},
|
||||
RuntimeProfile::GeneralAssistant => Self {
|
||||
requires_browser_surface: false,
|
||||
|
||||
Reference in New Issue
Block a user