优化策划agent #340
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@genarrative/ai-game-creator-shell",
|
||||
"private": true,
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.27",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/start-tauri-dev.mjs",
|
||||
|
||||
+1
-1
@@ -1725,7 +1725,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "genarrative-ai-game-creator-shell"
|
||||
version = "0.1.12"
|
||||
version = "0.1.27"
|
||||
dependencies = [
|
||||
"agent-runtime-core",
|
||||
"axum",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "genarrative-ai-game-creator-shell"
|
||||
version = "0.1.12"
|
||||
version = "0.1.27"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
||||
@@ -229,15 +229,18 @@ fn prepare_design_decision(
|
||||
}
|
||||
if approved {
|
||||
let phase = approve_design_phase(session, request_id)?;
|
||||
let suffix = if phase == "consultant" {
|
||||
""
|
||||
} else {
|
||||
"请开始该阶段工作。"
|
||||
};
|
||||
append_design_user(
|
||||
session,
|
||||
id,
|
||||
format!("用户已批准上一阶段,现在进入 {phase} 阶段。{suffix}"),
|
||||
if phase == "consultant" {
|
||||
format!(
|
||||
"用户已批准上一阶段,现在进入 {phase} 阶段。开始本轮工作前,先调用 get_workflow_status 确认 Runtime 当前阶段;在工具返回前,不要开始顾问工作或断言阶段状态。"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"用户已批准上一阶段,现在进入 {phase} 阶段。开始本轮工作前,先调用 get_workflow_status 确认 Runtime 当前阶段;在工具返回前,不要开始新阶段工作、写入文件、提交审批或断言阶段状态。"
|
||||
)
|
||||
},
|
||||
);
|
||||
begin_design_turn(session, id);
|
||||
} else {
|
||||
@@ -473,7 +476,7 @@ fn design_debug(root: &Path, kind: &str, data: Value) {
|
||||
type Entry = (PathBuf, Value);
|
||||
static QUEUE: OnceLock<std::sync::mpsc::SyncSender<Entry>> = OnceLock::new();
|
||||
let sender = QUEUE.get_or_init(|| {
|
||||
let (sender, receiver) = std::sync::mpsc::sync_channel::<Entry>(16);
|
||||
let (sender, receiver) = std::sync::mpsc::sync_channel::<Entry>(256);
|
||||
let _ = std::thread::Builder::new()
|
||||
.name("design-debug".into())
|
||||
.spawn(move || {
|
||||
@@ -546,8 +549,26 @@ async fn request_design_provider(
|
||||
None,
|
||||
));
|
||||
let result = if llm.stream {
|
||||
let mut stream_sequence = 0_u64;
|
||||
client
|
||||
.stream_run(request.clone(), |delta| {
|
||||
stream_sequence = stream_sequence.saturating_add(1);
|
||||
design_debug(
|
||||
root,
|
||||
"stream",
|
||||
json!({
|
||||
"turnId": turn_id,
|
||||
"requestIndex": session.turn.as_ref().map(|turn| turn.request_index),
|
||||
"attempt": attempt,
|
||||
"sequence": stream_sequence,
|
||||
"occurredAtUnixNanos": unix_timestamp_nanos().to_string(),
|
||||
"model": llm.model,
|
||||
"deltaChars": delta.delta_text.chars().count(),
|
||||
"accumulatedChars": delta.accumulated_text.chars().count(),
|
||||
"deltaText": delta.delta_text,
|
||||
"finishReason": delta.finish_reason,
|
||||
}),
|
||||
);
|
||||
emit(design_event(
|
||||
root,
|
||||
&turn_id,
|
||||
|
||||
@@ -1501,7 +1501,7 @@ const GAME_CREATOR_APP_CONFIG_SCHEMA_VERSION: &str = "game-creator-config.v2";
|
||||
const DEFAULT_GAME_CREATOR_LLM_BASE_URL: &str = "https://dev.genarrative.world/gpt/v1";
|
||||
const DEFAULT_GAME_CREATOR_LLM_MODEL: &str = "gpt-6-astra";
|
||||
const DEFAULT_GAME_CREATOR_LLM_API_KIND: &str = "openai_responses";
|
||||
const DEFAULT_GAME_CREATOR_LLM_REASONING_EFFORT: &str = "max";
|
||||
const DEFAULT_GAME_CREATOR_LLM_REASONING_EFFORT: &str = "high";
|
||||
const DEFAULT_GAME_CREATOR_LLM_CONTEXT_WINDOW_TOKENS: u64 = 128_000;
|
||||
const DEFAULT_GAME_CREATOR_LLM_AUTO_COMPACT_TOKEN_LIMIT: u64 = 64_000;
|
||||
const DEFAULT_GAME_CREATOR_LLM_TOOL_OUTPUT_TOKEN_LIMIT: u64 = 12_000;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Genarrative AI Game Creator",
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.27",
|
||||
"identifier": "world.genarrative.ai-game-creator",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm --prefix ../.. run agc:serve",
|
||||
|
||||
@@ -817,7 +817,10 @@ export function App({
|
||||
null,
|
||||
);
|
||||
planningV2SessionRef.current = planningV2Session;
|
||||
const [planningV2TransientReply, setPlanningV2TransientReply] = useState('');
|
||||
const [planningV2TransientReply, setPlanningV2TransientReplyVisible] =
|
||||
useState('');
|
||||
const planningV2TransientReplyTargetRef = useRef('');
|
||||
const planningV2VisibleReplyRef = useRef('');
|
||||
const [planningV2Reasoning, setPlanningV2Reasoning] = useState('');
|
||||
const planningV2TurnRef = useRef<{
|
||||
projectPath: string;
|
||||
@@ -838,6 +841,37 @@ export function App({
|
||||
>(),
|
||||
);
|
||||
|
||||
function setPlanningV2TransientReplyTarget(next: string) {
|
||||
planningV2TransientReplyTargetRef.current = next;
|
||||
if (!next) {
|
||||
planningV2VisibleReplyRef.current = '';
|
||||
setPlanningV2TransientReplyVisible('');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => {
|
||||
const target = planningV2TransientReplyTargetRef.current;
|
||||
setPlanningV2TransientReplyVisible((current) => {
|
||||
if (!target) {
|
||||
planningV2VisibleReplyRef.current = '';
|
||||
return '';
|
||||
}
|
||||
const prefix = target.startsWith(current) ? current : '';
|
||||
if (prefix === target) {
|
||||
planningV2VisibleReplyRef.current = target;
|
||||
return target;
|
||||
}
|
||||
const remaining = target.length - prefix.length;
|
||||
const step = remaining > 160 ? 4 : remaining > 48 ? 2 : 1;
|
||||
const next = target.slice(0, prefix.length + step);
|
||||
planningV2VisibleReplyRef.current = next;
|
||||
return next;
|
||||
});
|
||||
}, 50);
|
||||
return () => window.clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
function applyPlanningV2CommandResult(
|
||||
result: PlanningSessionCommandResultV2,
|
||||
clientTurnId?: string,
|
||||
@@ -1006,7 +1040,7 @@ export function App({
|
||||
};
|
||||
setChatAgentBusy(true);
|
||||
setProjectSupervisorRuntimeError('');
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
try {
|
||||
const view = await invoke<DesignView>('continue_design_agent_session', {
|
||||
projectPath: nextProjectPath,
|
||||
@@ -1029,7 +1063,7 @@ export function App({
|
||||
setPlanGddError(message);
|
||||
} finally {
|
||||
designAgentTurnRef.current = null;
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setChatAgentBusy(false);
|
||||
}
|
||||
}
|
||||
@@ -1520,7 +1554,7 @@ export function App({
|
||||
setProjectSupervisorResponseStream(null);
|
||||
setProjectSupervisorRuntimeError('');
|
||||
setPlanningV2Session(null);
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setPlanningV2Active(planningStartMode);
|
||||
planningV2ActiveRef.current = planningStartMode;
|
||||
designAgentLaneRef.current = planningStartMode;
|
||||
@@ -1914,7 +1948,7 @@ export function App({
|
||||
return;
|
||||
}
|
||||
if (payload.status === 'started') {
|
||||
setPlanningV2TransientReply(
|
||||
setPlanningV2TransientReplyTarget(
|
||||
payload.accumulatedText || '正在生成策划方案…',
|
||||
);
|
||||
setProjectSupervisorRuntimeError('');
|
||||
@@ -1923,7 +1957,7 @@ export function App({
|
||||
payload.status === 'delta' &&
|
||||
(payload.accumulatedText || payload.deltaText)
|
||||
) {
|
||||
setPlanningV2TransientReply(
|
||||
setPlanningV2TransientReplyTarget(
|
||||
payload.accumulatedText || payload.deltaText,
|
||||
);
|
||||
setProjectSupervisorRuntimeError('');
|
||||
@@ -1971,13 +2005,13 @@ export function App({
|
||||
return;
|
||||
}
|
||||
if (payload.kind === 'text' && payload.text != null) {
|
||||
setPlanningV2TransientReply(payload.text);
|
||||
setPlanningV2TransientReplyTarget(payload.text);
|
||||
}
|
||||
if (payload.reasoningText != null) {
|
||||
setPlanningV2Reasoning(payload.reasoningText);
|
||||
}
|
||||
if (payload.kind === 'tool' && payload.text) {
|
||||
setPlanningV2TransientReply(payload.text);
|
||||
setPlanningV2TransientReplyTarget(payload.text);
|
||||
}
|
||||
if (payload.view) {
|
||||
applyDesignView(payload.view, payload.projectPath);
|
||||
@@ -6037,7 +6071,7 @@ export function App({
|
||||
};
|
||||
setChatAgentBusy(true);
|
||||
setProjectSupervisorRuntimeError('');
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
try {
|
||||
const result = currentSessionId
|
||||
? await invoke<PlanningSessionCommandResultV2>(
|
||||
@@ -6090,7 +6124,7 @@ export function App({
|
||||
]);
|
||||
} finally {
|
||||
planningV2TurnRef.current = null;
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setChatAgentBusy(false);
|
||||
}
|
||||
}
|
||||
@@ -11790,7 +11824,7 @@ export function App({
|
||||
projectPath: nextProjectPath,
|
||||
clientTurnId,
|
||||
};
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setChatAgentBusy(true);
|
||||
setPlanGddDecisionBusy(true);
|
||||
void invoke<DesignView>('decide_design_phase', {
|
||||
@@ -11831,7 +11865,7 @@ export function App({
|
||||
return;
|
||||
}
|
||||
designAgentTurnRef.current = null;
|
||||
setPlanningV2TransientReply('');
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setChatAgentBusy(false);
|
||||
setPlanGddDecisionBusy(false);
|
||||
});
|
||||
|
||||
@@ -25,23 +25,20 @@ type DesignAgentSurfaceProps = {
|
||||
onRetry: () => void;
|
||||
};
|
||||
|
||||
export function DesignAgentSurface({
|
||||
type DesignAgentControlsProps = Pick<
|
||||
DesignAgentSurfaceProps,
|
||||
'view' | 'busy' | 'error' | 'onApprove' | 'onClarify' | 'onRetry'
|
||||
>;
|
||||
|
||||
export function DesignAgentPhaseStatus({
|
||||
view,
|
||||
busy,
|
||||
error,
|
||||
onApprove,
|
||||
onClarify,
|
||||
onRetry,
|
||||
}: DesignAgentSurfaceProps) {
|
||||
const [clarifyText, setClarifyText] = useState('');
|
||||
busy,
|
||||
}: DesignAgentControlsProps) {
|
||||
const phase = view?.session.currentPhase ?? 'concept';
|
||||
const pending = view?.session.pendingApproval;
|
||||
const clarification = view?.session.pendingClarification;
|
||||
useLayoutEffect(() => {
|
||||
setClarifyText('');
|
||||
}, [clarification?.requestId]);
|
||||
return (
|
||||
<section className="design-agent-controls" aria-label="策划阶段控制">
|
||||
<section className="design-agent-controls" aria-label="策划阶段状态">
|
||||
<div className="design-agent-controls__header">
|
||||
<div>
|
||||
<span>当前阶段</span>
|
||||
@@ -54,6 +51,42 @@ export function DesignAgentSurface({
|
||||
) : null}
|
||||
</div>
|
||||
{error ? <p className="design-agent-controls__error">{error}</p> : null}
|
||||
{view?.canRetry ? (
|
||||
<button
|
||||
className="design-agent-retry"
|
||||
type="button"
|
||||
disabled={busy}
|
||||
onClick={onRetry}
|
||||
>
|
||||
<RotateCcw size={15} aria-hidden="true" />
|
||||
重试本轮
|
||||
</button>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function DesignAgentPendingActions({
|
||||
view,
|
||||
busy,
|
||||
onApprove,
|
||||
onClarify,
|
||||
}: DesignAgentControlsProps) {
|
||||
const [clarifyText, setClarifyText] = useState('');
|
||||
const phase = view?.session.currentPhase ?? 'concept';
|
||||
const pending = view?.session.pendingApproval;
|
||||
const clarification = view?.session.pendingClarification;
|
||||
useLayoutEffect(() => {
|
||||
setClarifyText('');
|
||||
}, [clarification?.requestId]);
|
||||
if (!pending || phase === 'consultant') {
|
||||
if (!clarification) return null;
|
||||
}
|
||||
return (
|
||||
<section
|
||||
className="design-agent-pending-actions"
|
||||
aria-label="策划待处理事项"
|
||||
>
|
||||
{pending && phase !== 'consultant' ? (
|
||||
<div className="design-agent-approval">
|
||||
<p>当前阶段产物已提交,是否批准进入下一阶段?</p>
|
||||
@@ -110,17 +143,26 @@ export function DesignAgentSurface({
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{view?.canRetry ? (
|
||||
<button
|
||||
className="design-agent-retry"
|
||||
type="button"
|
||||
disabled={busy}
|
||||
onClick={onRetry}
|
||||
>
|
||||
<RotateCcw size={15} aria-hidden="true" />
|
||||
重试本轮
|
||||
</button>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function DesignAgentSurface({
|
||||
view,
|
||||
busy,
|
||||
error,
|
||||
onApprove,
|
||||
onClarify,
|
||||
onRetry,
|
||||
}: DesignAgentSurfaceProps) {
|
||||
return (
|
||||
<DesignAgentPhaseStatus
|
||||
view={view}
|
||||
busy={busy}
|
||||
error={error}
|
||||
onApprove={onApprove}
|
||||
onClarify={onClarify}
|
||||
onRetry={onRetry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+15
-2
@@ -31,7 +31,10 @@ import {
|
||||
ConversationModelSelect,
|
||||
type ConversationModelSelectHandle,
|
||||
} from './ConversationModelSelect';
|
||||
import { DesignAgentSurface } from './DesignAgentSurface';
|
||||
import {
|
||||
DesignAgentPendingActions,
|
||||
DesignAgentPhaseStatus,
|
||||
} from './DesignAgentSurface';
|
||||
import { PlanGddSurface } from './GddApprovalCard';
|
||||
import {
|
||||
pendingCommandDetail,
|
||||
@@ -212,7 +215,7 @@ export function ProjectSupervisorView({
|
||||
>
|
||||
<div className="project-supervisor-conversation">
|
||||
{designView || onDesignApprove ? (
|
||||
<DesignAgentSurface
|
||||
<DesignAgentPhaseStatus
|
||||
view={designView}
|
||||
busy={runtimePanelProps.controlBusy || planGddDecisionBusy}
|
||||
error={planGddError}
|
||||
@@ -393,6 +396,16 @@ export function ProjectSupervisorView({
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{designView || onDesignApprove ? (
|
||||
<DesignAgentPendingActions
|
||||
view={designView}
|
||||
busy={runtimePanelProps.controlBusy || planGddDecisionBusy}
|
||||
error={planGddError}
|
||||
onApprove={onDesignApprove ?? (() => undefined)}
|
||||
onClarify={onDesignClarify ?? (() => undefined)}
|
||||
onRetry={onDesignRetry ?? (() => undefined)}
|
||||
/>
|
||||
) : null}
|
||||
<form
|
||||
className={`project-supervisor-composer${
|
||||
directCodex ? ' is-direct-codex' : ''
|
||||
|
||||
@@ -52,7 +52,7 @@ const defaultRuntimeConfigDraft: GameCreatorAppConfig = {
|
||||
baseUrl: '',
|
||||
model: '',
|
||||
apiKind: 'openai_responses',
|
||||
reasoningEffort: 'max',
|
||||
reasoningEffort: 'high',
|
||||
stream: true,
|
||||
webSearchEnabled: true,
|
||||
contextWindowTokens: 128000,
|
||||
|
||||
@@ -9754,6 +9754,12 @@ button.design-workspace-tree__entry:hover,
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.design-agent-pending-actions {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.design-agent-approval p,
|
||||
.design-agent-clarify p {
|
||||
margin: 0;
|
||||
|
||||
@@ -798,7 +798,7 @@ function createProjectSupervisorRuntimeHarness({
|
||||
baseUrl: '',
|
||||
model: 'quality',
|
||||
apiKind: 'openai_responses',
|
||||
reasoningEffort: 'max',
|
||||
reasoningEffort: 'high',
|
||||
stream: true,
|
||||
webSearchEnabled: true,
|
||||
contextWindowTokens: 128000,
|
||||
|
||||
@@ -1000,7 +1000,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
expect(screen.queryByLabelText('LLM API Key')).toBeNull();
|
||||
expect(screen.queryByLabelText('LLM Base URL')).toBeNull();
|
||||
expect(screen.queryByLabelText('LLM 模型')).toBeNull();
|
||||
expect(screen.getByLabelText('推理档')).toHaveProperty('value', 'max');
|
||||
expect(screen.getByLabelText('推理档')).toHaveProperty('value', 'high');
|
||||
expect(screen.getByLabelText('联网检索')).toHaveProperty('checked', true);
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
@@ -1031,7 +1031,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
baseUrl: '',
|
||||
model: '',
|
||||
apiKind: 'openai_responses',
|
||||
reasoningEffort: 'max',
|
||||
reasoningEffort: 'high',
|
||||
stream: true,
|
||||
webSearchEnabled: true,
|
||||
contextWindowTokens: 128000,
|
||||
|
||||
Generated
+1
-1
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"apps/ai-game-creator-shell": {
|
||||
"name": "@genarrative/ai-game-creator-shell",
|
||||
"version": "0.1.12",
|
||||
"version": "0.1.27",
|
||||
"dependencies": {
|
||||
"@cubone/react-file-manager": "^1.35.0",
|
||||
"@genarrative/image-canvas-core": "0.1.0",
|
||||
|
||||
Reference in New Issue
Block a user