A server brain without hands calls its server the brain server, and tells the model which computers really exist
This commit is contained in:
parent
dabb4113c2
commit
591626408f
32
src/app.rs
32
src/app.rs
|
|
@ -817,6 +817,15 @@ impl App {
|
|||
}
|
||||
v[i].name = crate::model::THIS_COMPUTER_NAME.into();
|
||||
}
|
||||
} else if self.headless {
|
||||
// A server brain with no hands connected: the machine it runs on is a server,
|
||||
// not anyone's computer. Wearing the name 你的電腦 here made bots "work on your
|
||||
// computer" on the server while the person watched their own untouched desktop.
|
||||
for t in v.iter_mut() {
|
||||
if matches!(t.place, tools::Place::Local { .. }) {
|
||||
t.name = crate::model::BRAIN_HOST_NAME.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v
|
||||
|
|
@ -827,7 +836,7 @@ impl App {
|
|||
self.targets_for(b)
|
||||
.iter()
|
||||
.map(|t| match &t.place {
|
||||
tools::Place::Sealed { .. } | tools::Place::Box { .. } => format!("{}:你自己的電腦,一律可用", t.name),
|
||||
tools::Place::Sealed { .. } | tools::Place::Box { .. } => format!("{}:這個 Bot 自己的 Linux 桌面(AI 的電腦,不是使用者的電腦),一律可用", t.name),
|
||||
tools::Place::Local { .. } if t.name == crate::model::BRAIN_HOST_NAME => format!("{}:AnyBot 大腦所在的伺服器「{}」(不是使用者的電腦)", t.name, self.state.remote.host_name),
|
||||
tools::Place::Local { .. } => format!(
|
||||
"{}:{}「{}」,{}",
|
||||
|
|
@ -3278,6 +3287,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn serve(ctx: &egui::Context) -> ! {
|
||||
crate::appctl::SERVING.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut app = App::new(ctx, None);
|
||||
app.headless = true;
|
||||
if let Ok(pw) = std::env::var("ANYBOT_PHONE_PASSWORD") {
|
||||
|
|
@ -3679,6 +3689,24 @@ fn system_prompt(b: &Bot, st: &State, computers: &[tools::Target], default_compu
|
|||
s.push_str("你是使用者的主要 Bot:主要聯絡窗口,可以主動幫忙,也能用 message_bot 把工作交給其他 Bot。");
|
||||
}
|
||||
let now = crate::routines::now_in(&st.timezone);
|
||||
// Which screens can really be operated, named as the model must name them: the fixed
|
||||
// wording promised "你的電腦(使用者的 Mac)" on server brains that have no such computer,
|
||||
// and bots then "worked on your computer" on the server itself.
|
||||
let mut screens: Vec<String> = Vec::new();
|
||||
for t in computers {
|
||||
match &t.place {
|
||||
tools::Place::Box { .. } => screens.push(format!("{}(AI 自己的 Linux 桌面,不是使用者的電腦)", t.name)),
|
||||
tools::Place::Local { .. } => {
|
||||
if crate::computer::screen_supported() {
|
||||
screens.push(format!("{}(這台電腦自己的畫面)", t.name));
|
||||
}
|
||||
}
|
||||
tools::Place::Device { host } if t.name == crate::model::THIS_COMPUTER_NAME => screens.push(format!("{}(使用者自己的電腦「{}」的畫面)", t.name, crate::remote::peer_name(host))),
|
||||
tools::Place::Device { .. } => screens.push(format!("{}(帳號裡另一台電腦的畫面)", t.name)),
|
||||
tools::Place::Ssh { .. } | tools::Place::Sealed { .. } => {}
|
||||
}
|
||||
}
|
||||
let screens = if screens.is_empty() { "目前沒有任何可操作的螢幕".to_string() } else { screens.join(";") };
|
||||
s.push_str(&format!(
|
||||
"\n\n現在是 {}({},時區 {})。\n\
|
||||
你在 AnyBot 這個 App 裡,可以直接操作 App:\n\
|
||||
|
|
@ -3694,7 +3722,7 @@ fn system_prompt(b: &Bot, st: &State, computers: &[tools::Target], default_compu
|
|||
- 絕對不要直接呼叫網站的內部 API、下載網站的程式碼來研究,或用程式大量抓資料:網站會把帳號當成機器人(例如跳出拼圖驗證、封鎖帳號)。出現驗證碼、拼圖或簡訊驗證時,停下來請使用者在 AnyBot 瀏覽器視窗完成。
|
||||
- 如果讀到的是登入頁,請使用者在跳出來的 AnyBot 瀏覽器視窗登入一次(之後會記住),登入好再讀。絕對不要叫使用者去改瀏覽器的設定或選單(例如「允許來自 Apple 事件的 JavaScript」),那不需要。也不要說要從 Chrome 複製登入或 Cookie,沒有這個功能。\n\
|
||||
- AnyBot 瀏覽器有設定檔(profile),每個有自己的登入。使用者說要換 profile/設定檔/另一個帳號時,用 browser_profiles:list 看有哪些,use 切換,add 新增(新的要登入一次)。也可以在 browser_open / browser_read 帶 profile 參數。使用者想用自己平常的 Chrome(自己的 Chrome 設定檔、已經登入的帳號)時,用 profile「你的瀏覽器」:請使用者在 Chrome 切到想要的設定檔,再 browser_read。\n\
|
||||
- 你也能像人一樣操作螢幕:你自己的電腦(Bot 的電腦,Linux 桌面)或使用者的 Mac(你的電腦,用在使用者自己的 App)。computer_use_screenshot 看畫面,再用 computer_use_click / computer_use_typing / computer_use_press_key / computer_use_scroll / computer_use_drag 操作;座標用最新截圖上的像素。每做一步就再截圖確認。用在檔案和網頁工具做不到的事(例如操作其他 App、在 Chrome 切換設定檔)。\n\
|
||||
- 你也能像人一樣操作螢幕({screens})。computer_use_screenshot 看畫面,再用 computer_use_click / computer_use_typing / computer_use_press_key / computer_use_scroll / computer_use_drag 操作;座標用最新截圖上的像素。每做一步就再截圖確認。用在檔案和網頁工具做不到的事(例如操作其他 App、在 Chrome 切換設定檔)。\n\
|
||||
- 使用者 Mac 上的「提醒事項、行事曆、聯絡人、備忘錄、郵件、訊息」有專門的工具(ios_read_reminders、mobile_read_calendar、mobile_read_contacts、mac_read_notes、mac_read_mail 等),比操作畫面快又準;寄信或傳訊息每次都會先請使用者確認,起草郵件(mac_draft_mail)則讓使用者自己按寄出。\n\
|
||||
- 不要說「我沒有瀏覽器或工具」:先試這些工具。需要權限時 App 會自己跳出視窗問使用者;工具回報要開什麼設定時,照實轉告。",
|
||||
now.format("%Y-%m-%d %H:%M"),
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@ pub fn needs_approval(name: &str, args: &str, state: &State) -> Option<String> {
|
|||
None
|
||||
}
|
||||
|
||||
/// True while running as a headless brain (`anybot --serve`): "this computer" is a server,
|
||||
/// not a person's desktop, and the computer-wording tools must say so. Set in App::serve.
|
||||
pub static SERVING: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
pub struct Outcome {
|
||||
pub text: String,
|
||||
pub ok: bool,
|
||||
|
|
@ -543,9 +547,16 @@ pub fn apply(state: &mut State, bot_id: u64, name: &str, args: &str) -> Outcome
|
|||
Outcome { text: format!("已交給「{name}」,它的回覆會出現在它自己的聊天裡。"), ok: true, wake: vec![(target, msg)], theme_changed: false, page: None }
|
||||
}
|
||||
"list_computers" => {
|
||||
// On a server brain (serve), Local is the server, not the person's computer —
|
||||
// matching what the app's own list_computers says after its rename.
|
||||
let serving = SERVING.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let mut lines = vec![
|
||||
format!("{}:你自己的電腦(獨立的資料夾),一律可用", model::BOT_COMPUTER_NAME),
|
||||
format!("{}:{},{}", model::THIS_COMPUTER_NAME, if cfg!(target_os = "macos") { "這台 Mac" } else { "這台電腦" }, if state.local_execution { "已允許" } else { "使用者還沒在設定裡允許(設定 → 電腦 → 在這台電腦上執行)" }),
|
||||
format!("{}:{}", model::BOT_COMPUTER_NAME, if serving { "這個 Bot 自己的 Linux 電腦(AI 的電腦,不是使用者的電腦,獨立資料夾),一律可用" } else { "你自己的電腦(獨立的資料夾),一律可用" }),
|
||||
if serving {
|
||||
format!("{}:AnyBot 大腦所在的伺服器(不是使用者的電腦)。使用者的電腦要在它上面裝 AnyBot 並登入同一帳號,才會以「{}」出現", model::BRAIN_HOST_NAME, model::THIS_COMPUTER_NAME)
|
||||
} else {
|
||||
format!("{}:{},{}", model::THIS_COMPUTER_NAME, if cfg!(target_os = "macos") { "這台 Mac" } else { "這台電腦" }, if state.local_execution { "已允許" } else { "使用者還沒在設定裡允許(設定 → 電腦 → 在這台電腦上執行)" })
|
||||
},
|
||||
];
|
||||
lines.extend(state.remote_computers.iter().map(|c| format!("{}:透過 SSH 連到 {}", c.name, c.ssh_target)));
|
||||
ok(lines.join("\n"))
|
||||
|
|
|
|||
|
|
@ -224,9 +224,24 @@ pub fn specs_for(computers: &[String], default: &str) -> Vec<Value> {
|
|||
// (a 手腳 computer): the bot should say whose screen it means.
|
||||
let own = [crate::model::BOT_COMPUTER_NAME, crate::model::THIS_COMPUTER_NAME];
|
||||
if computers.len() > 1 || computers.iter().any(|c| !own.contains(&c.as_str())) {
|
||||
// Explain each name that is actually offered (the old text promised "the person's Mac"
|
||||
// on machines where it does not exist, and the bot grabbed the next best thing).
|
||||
let mut parts = Vec::new();
|
||||
if computers.iter().any(|c| c == crate::model::BOT_COMPUTER_NAME) {
|
||||
parts.push(format!("{} is a private Linux desktop for the bot (the AI's own computer, not the person's).", crate::model::BOT_COMPUTER_NAME));
|
||||
}
|
||||
if computers.iter().any(|c| c == crate::model::THIS_COMPUTER_NAME) {
|
||||
parts.push(format!("{} is the person's own computer (use it for the person's own apps).", crate::model::THIS_COMPUTER_NAME));
|
||||
}
|
||||
if computers.iter().any(|c| c == crate::model::BRAIN_HOST_NAME) {
|
||||
parts.push(format!("{} is the server the brain runs on — not a person's screen; a person asking about \"my screen\" never means it.", crate::model::BRAIN_HOST_NAME));
|
||||
}
|
||||
if computers.iter().any(|c| c != crate::model::BOT_COMPUTER_NAME && c != crate::model::THIS_COMPUTER_NAME && c != crate::model::BRAIN_HOST_NAME) {
|
||||
parts.push("Any other name is another computer of the account.".to_string());
|
||||
}
|
||||
let desc = format!("Whose screen. Default: {default}. {}", parts.join(" "));
|
||||
for t in v.iter_mut() {
|
||||
t["parameters"]["properties"]["computer"] = json!({"type": "string", "enum": computers,
|
||||
"description": format!("Whose screen. Default: {default}. {} is the bot's own Linux desktop; {} is the person's Mac (use it for the person's own apps).", crate::model::BOT_COMPUTER_NAME, crate::model::THIS_COMPUTER_NAME)});
|
||||
t["parameters"]["properties"]["computer"] = json!({"type": "string", "enum": computers, "description": desc});
|
||||
}
|
||||
}
|
||||
v
|
||||
|
|
|
|||
10
src/tools.rs
10
src/tools.rs
|
|
@ -629,10 +629,18 @@ pub fn specs_for(computers: &[String], default: &str) -> Vec<Value> {
|
|||
if t["name"] == "read_terminal" || t["name"] == "write_terminal" {
|
||||
continue;
|
||||
}
|
||||
// None of these being the person's own computer (a server brain with no hands):
|
||||
// a bot that "helped on your computer" then worked on the server, while the person
|
||||
// watched their own desktop stay untouched. Say what is true instead.
|
||||
let desc = if computers.iter().any(|c| c == crate::model::THIS_COMPUTER_NAME) {
|
||||
format!("Which computer to work on. Default: {default}. Use the one the person names (\"在我的 Mac 上\" = {}).", crate::model::THIS_COMPUTER_NAME)
|
||||
} else {
|
||||
format!("Which computer to work on. Default: {default}. None of these is the person's own computer (it is not connected): use the default, and when the person means their own computer, tell them it is not connected (App › 設定 › 電腦) instead of working on another machine and calling it theirs.")
|
||||
};
|
||||
t["parameters"]["properties"]["computer"] = json!({
|
||||
"type": "string",
|
||||
"enum": computers,
|
||||
"description": format!("Which computer to work on. Default: {default}. Use the one the person names (\"在我的 Mac 上\" = {}).", crate::model::THIS_COMPUTER_NAME)
|
||||
"description": desc
|
||||
});
|
||||
}
|
||||
v
|
||||
|
|
|
|||
Loading…
Reference in New Issue