feat: add native master agent evolution center
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
<activity android:name=".MasterAgentPromptActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentTakeoverActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentMemoryActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".MasterAgentEvolutionActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".OpsCenterActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
<activity android:name=".AboutActivity" android:exported="false" android:screenOrientation="portrait" />
|
||||
|
||||
|
||||
@@ -265,6 +265,32 @@ public class BossApiClient {
|
||||
return requestWithRestore("GET", "/api/v1/projects/" + encode(projectId) + "/memories", null);
|
||||
}
|
||||
|
||||
public ApiResponse getMasterAgentEvolution() throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/master-agent/evolution", null);
|
||||
}
|
||||
|
||||
public ApiResponse updateMasterAgentEvolutionMode(String mode) throws IOException, JSONException {
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("mode", mode);
|
||||
return requestWithRestore("POST", "/api/v1/master-agent/evolution/config", payload);
|
||||
}
|
||||
|
||||
public ApiResponse approveMasterAgentEvolutionProposal(String proposalId) throws IOException, JSONException {
|
||||
return requestWithRestore(
|
||||
"POST",
|
||||
"/api/v1/master-agent/evolution/proposals/" + encode(proposalId) + "/approve",
|
||||
new JSONObject()
|
||||
);
|
||||
}
|
||||
|
||||
public ApiResponse rejectMasterAgentEvolutionProposal(String proposalId) throws IOException, JSONException {
|
||||
return requestWithRestore(
|
||||
"POST",
|
||||
"/api/v1/master-agent/evolution/proposals/" + encode(proposalId) + "/reject",
|
||||
new JSONObject()
|
||||
);
|
||||
}
|
||||
|
||||
public ApiResponse createMasterAgentMemory(String projectId, JSONObject payload) throws IOException, JSONException {
|
||||
return requestWithRestore("POST", "/api/v1/projects/" + encode(projectId) + "/memories", payload);
|
||||
}
|
||||
|
||||
@@ -1813,6 +1813,14 @@ public class MainActivity extends AppCompatActivity {
|
||||
case "security":
|
||||
intent = new Intent(this, SecurityActivity.class);
|
||||
break;
|
||||
case "master_agent_prompt":
|
||||
intent = new Intent(this, MasterAgentPromptActivity.class);
|
||||
intent.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_ID, "master-agent");
|
||||
intent.putExtra(MasterAgentPromptActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
break;
|
||||
case "master_agent_evolution":
|
||||
intent = new Intent(this, MasterAgentEvolutionActivity.class);
|
||||
break;
|
||||
case "ai_accounts":
|
||||
intent = new Intent(this, AiAccountsActivity.class);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class MasterAgentEvolutionActivity extends BossScreenActivity {
|
||||
private static final long REALTIME_RELOAD_THROTTLE_MS = 900L;
|
||||
|
||||
private LinearLayout contentRoot;
|
||||
private @Nullable BossRealtimeClient realtimeClient;
|
||||
private long lastRealtimeReloadAt;
|
||||
private boolean contentLoaded;
|
||||
private @Nullable String currentMode;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
configureScreen("主 Agent 自动进化", "信号、提案与生效规则");
|
||||
setHeaderAction("刷新", v -> reload());
|
||||
contentRoot = new LinearLayout(this);
|
||||
contentRoot.setOrientation(LinearLayout.VERTICAL);
|
||||
replaceContent(contentRoot);
|
||||
realtimeClient = new BossRealtimeClient(apiClient, this::handleRealtimeEvent);
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateRealtimeSubscription();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
stopRealtimeUpdates();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
stopRealtimeUpdates();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.getMasterAgentEvolution();
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> renderDashboard(response.json));
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
contentLoaded = false;
|
||||
replaceContent(BossUi.buildEmptyCard(this, "自动进化加载失败:" + error.getMessage()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateRealtimeSubscription() {
|
||||
if (apiClient != null && apiClient.hasSessionHints() && realtimeClient != null) {
|
||||
realtimeClient.start();
|
||||
return;
|
||||
}
|
||||
stopRealtimeUpdates();
|
||||
}
|
||||
|
||||
private void stopRealtimeUpdates() {
|
||||
if (realtimeClient != null) {
|
||||
realtimeClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void handleRealtimeEvent(BossRealtimeEvent event) {
|
||||
if (event == null || !"master_agent.settings.updated".equals(event.eventName)) {
|
||||
return;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastRealtimeReloadAt < REALTIME_RELOAD_THROTTLE_MS) {
|
||||
return;
|
||||
}
|
||||
lastRealtimeReloadAt = now;
|
||||
runOnUiThread(this::reload);
|
||||
}
|
||||
|
||||
private void renderDashboard(JSONObject payload) {
|
||||
JSONObject config = payload.optJSONObject("config");
|
||||
JSONArray signals = payload.optJSONArray("signals");
|
||||
JSONArray proposals = payload.optJSONArray("proposals");
|
||||
JSONArray rules = payload.optJSONArray("rules");
|
||||
|
||||
currentMode = config == null ? "controlled" : config.optString("mode", "controlled");
|
||||
boolean autoApplyLowRiskRules = config != null && config.optBoolean("autoApplyLowRiskRules", false);
|
||||
|
||||
replaceContent(contentRoot);
|
||||
contentRoot.removeAllViews();
|
||||
|
||||
contentRoot.addView(BossUi.buildSimpleProfileHeader(
|
||||
this,
|
||||
"主 Agent 自动进化",
|
||||
"最近在学什么、打算怎么改、已经生效了什么",
|
||||
"支持在这里切换 controlled / autonomous,并直接审核待处理提案。"
|
||||
));
|
||||
|
||||
contentRoot.addView(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"当前模式",
|
||||
"autonomous".equals(currentMode) ? "完全自我进化" : "受控自动进化",
|
||||
autoApplyLowRiskRules ? "低风险提案会自动采纳。" : "所有提案都需要人工确认。"
|
||||
));
|
||||
|
||||
Button controlledButton = BossUi.buildMiniActionButton(this, "切到受控模式", false);
|
||||
controlledButton.setEnabled(!"controlled".equals(currentMode));
|
||||
controlledButton.setOnClickListener(v -> switchMode("controlled"));
|
||||
Button autonomousButton = BossUi.buildMiniActionButton(this, "切到全自动模式", true);
|
||||
autonomousButton.setEnabled(!"autonomous".equals(currentMode));
|
||||
autonomousButton.setOnClickListener(v -> switchMode("autonomous"));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, controlledButton, autonomousButton));
|
||||
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"待处理提案",
|
||||
String.valueOf(countPendingProposals(proposals)) + " 条",
|
||||
"待审核的策略变更会在这里集中展示。",
|
||||
null,
|
||||
null
|
||||
));
|
||||
renderPendingProposals(proposals);
|
||||
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"最近信号",
|
||||
signals == null ? "0 条" : signals.length() + " 条",
|
||||
"主 Agent 最近捕获到的问题和自我修正线索。",
|
||||
null,
|
||||
null
|
||||
));
|
||||
renderSignals(signals);
|
||||
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
"已生效规则",
|
||||
rules == null ? "0 条" : rules.length() + " 条",
|
||||
"已经落进系统并开始影响主 Agent 行为的规则。",
|
||||
null,
|
||||
null
|
||||
));
|
||||
renderRules(rules);
|
||||
|
||||
contentLoaded = true;
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void renderPendingProposals(@Nullable JSONArray proposals) {
|
||||
if (proposals == null || proposals.length() == 0) {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "当前没有待审批提案。"));
|
||||
return;
|
||||
}
|
||||
boolean rendered = false;
|
||||
for (int i = 0; i < proposals.length(); i++) {
|
||||
JSONObject proposal = proposals.optJSONObject(i);
|
||||
if (proposal == null || !"pending_review".equals(proposal.optString("status", ""))) {
|
||||
continue;
|
||||
}
|
||||
rendered = true;
|
||||
String proposalId = proposal.optString("proposalId", "");
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
proposal.optString("title", "待审批提案"),
|
||||
proposal.optString("summary", "暂无摘要"),
|
||||
proposal.optString("proposalType", "-")
|
||||
+ " · " + proposal.optString("riskLevel", "-")
|
||||
+ " · " + proposal.optString("createdAt", "-"),
|
||||
null,
|
||||
null
|
||||
));
|
||||
Button rejectButton = BossUi.buildMiniActionButton(this, "拒绝", false);
|
||||
rejectButton.setOnClickListener(v -> reviewProposal(proposalId, false));
|
||||
Button approveButton = BossUi.buildMiniActionButton(this, "批准", true);
|
||||
approveButton.setOnClickListener(v -> reviewProposal(proposalId, true));
|
||||
contentRoot.addView(BossUi.buildInlineActionRow(this, rejectButton, approveButton));
|
||||
}
|
||||
if (!rendered) {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "当前没有待审批提案。"));
|
||||
}
|
||||
}
|
||||
|
||||
private void renderSignals(@Nullable JSONArray signals) {
|
||||
if (signals == null || signals.length() == 0) {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "当前还没有进化信号。"));
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < Math.min(signals.length(), 8); i++) {
|
||||
JSONObject signal = signals.optJSONObject(i);
|
||||
if (signal == null) continue;
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
signal.optString("kind", "signal"),
|
||||
signal.optString("requestText", ""),
|
||||
signal.optString("createdAt", "-"),
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void renderRules(@Nullable JSONArray rules) {
|
||||
if (rules == null || rules.length() == 0) {
|
||||
contentRoot.addView(BossUi.buildEmptyCard(this, "当前还没有已生效规则。"));
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < Math.min(rules.length(), 8); i++) {
|
||||
JSONObject rule = rules.optJSONObject(i);
|
||||
if (rule == null) continue;
|
||||
contentRoot.addView(BossUi.buildWechatMenuRow(
|
||||
this,
|
||||
rule.optString("ruleType", "rule"),
|
||||
rule.optString("sourceProposalId", "直接创建"),
|
||||
rule.optString("createdAt", "-"),
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private int countPendingProposals(@Nullable JSONArray proposals) {
|
||||
if (proposals == null) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (int i = 0; i < proposals.length(); i++) {
|
||||
JSONObject proposal = proposals.optJSONObject(i);
|
||||
if (proposal != null && "pending_review".equals(proposal.optString("status", ""))) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void switchMode(String mode) {
|
||||
if (!contentLoaded) {
|
||||
showMessage("自动进化尚未加载完成。");
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.updateMasterAgentEvolutionMode(mode);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage("已切到 " + ("autonomous".equals(mode) ? "完全自我进化" : "受控自动进化"));
|
||||
setResult(RESULT_OK);
|
||||
reload();
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage("切换失败:" + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reviewProposal(String proposalId, boolean approve) {
|
||||
if (proposalId == null || proposalId.isEmpty()) {
|
||||
showMessage("缺少 proposalId");
|
||||
return;
|
||||
}
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = approve
|
||||
? apiClient.approveMasterAgentEvolutionProposal(proposalId)
|
||||
: apiClient.rejectMasterAgentEvolutionProposal(proposalId);
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
showMessage(approve ? "提案已批准" : "提案已拒绝");
|
||||
setResult(RESULT_OK);
|
||||
reload();
|
||||
});
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> {
|
||||
setRefreshing(false);
|
||||
showMessage((approve ? "批准失败:" : "拒绝失败:") + error.getMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,7 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
private ActivityResultLauncher<Intent> masterAgentPromptLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentTakeoverLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentMemoryLauncher;
|
||||
private ActivityResultLauncher<Intent> masterAgentEvolutionLauncher;
|
||||
private ActivityResultLauncher<Intent> forwardTargetLauncher;
|
||||
private ActivityResultLauncher<String> imagePickerLauncher;
|
||||
private ActivityResultLauncher<String> videoPickerLauncher;
|
||||
@@ -259,6 +260,14 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
}
|
||||
}
|
||||
);
|
||||
masterAgentEvolutionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == RESULT_OK) {
|
||||
reload(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
forwardTargetLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
@@ -1358,12 +1367,17 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
masterAgentTakeoverLauncher.launch(intent);
|
||||
}
|
||||
|
||||
private void openMasterAgentEvolution() {
|
||||
Intent intent = new Intent(this, MasterAgentEvolutionActivity.class);
|
||||
masterAgentEvolutionLauncher.launch(intent);
|
||||
}
|
||||
|
||||
private void showMasterAgentMoreMenu() {
|
||||
if (!isMasterAgentConversation()) {
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(new CharSequence[]{"模型", "推理强度", "全局接管", "提示词", "记忆", "会话信息", "刷新"}, (dialog, which) -> {
|
||||
.setItems(new CharSequence[]{"模型", "推理强度", "全局接管", "自动进化", "提示词", "记忆", "会话信息", "刷新"}, (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0:
|
||||
showMasterAgentModelPicker();
|
||||
@@ -1375,15 +1389,18 @@ public class ProjectDetailActivity extends BossScreenActivity {
|
||||
openMasterAgentTakeoverSettings();
|
||||
break;
|
||||
case 3:
|
||||
openMasterAgentPromptProfile();
|
||||
openMasterAgentEvolution();
|
||||
break;
|
||||
case 4:
|
||||
openMasterAgentMemories();
|
||||
openMasterAgentPromptProfile();
|
||||
break;
|
||||
case 5:
|
||||
openConversationInfo();
|
||||
openMasterAgentMemories();
|
||||
break;
|
||||
case 6:
|
||||
openConversationInfo();
|
||||
break;
|
||||
case 7:
|
||||
reload(true);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -19,6 +19,8 @@ public final class WechatSurfaceMapper {
|
||||
);
|
||||
|
||||
private static final List<MeMenuItem> ROOT_ME_MENU_ITEMS = Arrays.asList(
|
||||
new MeMenuItem("master_agent_prompt", "主 Agent 提示词 / 记忆", "配置全局主提示词、当前主提示词和用户记忆"),
|
||||
new MeMenuItem("master_agent_evolution", "主 Agent 自动进化", "查看进化信号、提案与自动采纳规则"),
|
||||
new MeMenuItem("security", "账号与安全", "修改登录密码、设备安全与身份校验"),
|
||||
new MeMenuItem("settings", "设置", "默认首页、提醒方式与危险操作确认"),
|
||||
new MeMenuItem("ops", "运维与修复", "查看运维会话、修复回放与 standby 切换"),
|
||||
|
||||
Reference in New Issue
Block a user