Update loop controller and service discovery

This commit is contained in:
Liang Jiaqing committed 2026-06-27 14:57:10 +08:00
1 parent c25ea7c15c
commit 18b4a791f0
2 files changed
+38 -12

No files matched your search

+36 -10
View File
@@ -71,6 +71,27 @@ def init():
agent = init()
def build_prompt(objective):
return f"""读取 {agent.log_path} 尾部,获取 agent 的最新输出。
用户的 loop 诉求:<objective>{objective}</objective>
判断该 agent 是否偷懒、是否真正完成诉求,用 <next_prompt></next_prompt> 输出要追加给它的指令:
一般复述 objective,或不超过 10 字的督促,如:别停,继续 / 这就叫最优?你优化到位了吗 / 看我要求,你达成了吗 / 你好好看清楚 / 你能不能看看记忆
只输出 <next_prompt>…</next_prompt>。"""
@st.cache_resource
def get_controller():
b = {'ev': threading.Event(), 'obj': '', 'out': None, 'ready': False}
def loop():
ag = GeneraticAgent(); ag.verbose = False; ag.log_path = False
threading.Thread(target=ag.run, daemon=True).start()
while True:
b['ev'].wait(); b['ev'].clear()
dq = ag.put_task(build_prompt(b['obj']), source="controller")
while 'done' not in (it := dq.get()): pass
ms = re.findall(r'<next_prompt>(.*?)</next_prompt>', it['done'], re.S)
b['out'] = ms[-1].strip() if ms else None; b['ready'] = True
threading.Thread(target=loop, daemon=True).start(); return b
st.title("🖥️ Cowork")
st.session_state.setdefault('autonomous_enabled', False)
@@ -119,9 +140,7 @@ def render_sidebar():
field-sizing: content; min-height: 1.6em !important; height: auto !important;
}
</style>""", unsafe_allow_html=True)
def _sync_loop_prompt():
st.session_state.loop_prompt = st.session_state.loop_prompt_input
loop_prompt = st.text_area("Loop prompt", value=st.session_state.get('loop_prompt', "继续" if LANG=='zh' else 'next'), key="loop_prompt_input", height=1, on_change=_sync_loop_prompt)
st.text_area("Loop prompt", value=st.session_state.get('loop_prompt_input', "继续" if LANG=='zh' else 'next'), key="loop_prompt_input", height=1)
if st.session_state.get('loop_enabled'):
if st.button("⏹️ Stop Loop"):
st.session_state.loop_enabled = False
@@ -130,8 +149,8 @@ def render_sidebar():
else:
if st.button("🔁 Loop!"):
st.session_state.loop_enabled = True
st.session_state.loop_prompt = loop_prompt
st.session_state['_inject_prompt'] = loop_prompt
get_controller()
st.session_state['_inject_prompt'] = st.session_state.get('loop_prompt_input', '')
st.toast("🔁 Looping"); st.rerun(scope="app")
st.divider()
if st.button(T('auto_start')):
@@ -252,10 +271,10 @@ def render_main_stream(prompt=None):
if response:
st.session_state.messages.append({"role": "assistant", "content": response})
st.session_state.last_reply_time = int(time.time())
# ── 循环回调:回答完成后自动注入下一条 ──
# ── 循环回调:回答完成戳醒 controller 决策(去程,现取最新objective) ──
if st.session_state.get('loop_enabled'):
st.session_state['_inject_prompt'] = st.session_state.get('loop_prompt', '继续')
st.rerun()
b = get_controller()
b['obj'] = st.session_state.get('loop_prompt_input', ''); b['ready'] = False; b['ev'].set()
if "messages" not in st.session_state: st.session_state.messages = []
for msg in st.session_state.messages:
@@ -365,11 +384,18 @@ elif st.session_state.get('display_queue') is not None:
render_main_stream()
# ── 空闲自主行动:fragment 定时检测,替代 launch.pyw 的 idle_monitor ──
@st.fragment(run_every=timedelta(minutes=5))
@st.fragment(run_every=timedelta(minutes=1))
def _idle_checker():
if st.session_state.get('loop_enabled'): # 循环回程:取controller回填
b = get_controller()
if b['ready']:
b['ready'] = False
if b['out']: st.session_state['_inject_prompt'] = b['out']
else: st.session_state.loop_enabled = False
st.rerun(scope="app")
return
if not st.session_state.get('autonomous_enabled'): return
if st.session_state.get('display_queue') is not None: return # 正在运行中
if st.session_state.get('loop_enabled'): return # 循环模式自己管
last = st.session_state.get('last_reply_time', int(time.time()))
if time.time() - last > 1800:
st.session_state['_inject_prompt'] = T('auto_prompt')
+2 -2
View File
@@ -17,7 +17,7 @@ def acquire_singleton():
def discover_services():
services = []
EXCLUDES = {'goal_mode.py', 'chatapp_common.py', 'tuiapp.py'}
EXCLUDES = {'goal_mode.py', 'chatapp_common.py', 'tuiapp.py', 'tui', '_master'}
reflect_dir = os.path.join(BASE_DIR, 'reflect')
if os.path.isdir(reflect_dir):
for f in sorted(os.listdir(reflect_dir)):
@@ -29,7 +29,7 @@ def discover_services():
frontends_dir = os.path.join(BASE_DIR, 'frontends')
if os.path.isdir(frontends_dir):
for f in sorted(os.listdir(frontends_dir)):
if 'app' in f and f.endswith('.py') and f not in EXCLUDES:
if 'app' in f and f.endswith('.py') and len([x for x in EXCLUDES if x in f]) == 0:
if 'stapp' in f: cmd = [sys.executable, '-m', 'streamlit', 'run', 'frontends/' + f, '--server.headless=true']
else: cmd = [sys.executable, 'frontends/' + f]
services.append({'name': 'frontends/' + f, 'cmd': cmd})