From 01c477c05054a6d4a3e2357f1e0923467ec74837 Mon Sep 17 00:00:00 2001 From: Jiaqing Liang Date: Fri, 30 Jan 2026 14:51:52 +0800 Subject: [PATCH] chore: self-commit core logic updates and agent state --- agent_loop.py | 2 +- agentapp.py | 44 +++------------------------------ ga.py | 18 +++++++++++--- sidercall.py | 68 ++++++++++++++++++++++++++++++++++----------------- simphtml.py | 4 +-- 5 files changed, 68 insertions(+), 68 deletions(-) diff --git a/agent_loop.py b/agent_loop.py index f1d2c38..7f53380 100644 --- a/agent_loop.py +++ b/agent_loop.py @@ -48,7 +48,7 @@ def agent_runner_loop(client, system_prompt, user_input, handler, tools_schema, response = client.chat(messages=messages, tools=tools_schema) if response.thinking: yield '' + response.thinking + '\n\n' - if '```' in response.content: response.content = response.content.replace('```', ' \n```') + if '```' in response.content: response.content = response.content.replace('```', '\n```') showcontent = response.content if '' in showcontent: showcontent = re.sub(r'\s*(.*?)\s*', r'\n````\n\n\1\n\n````', showcontent, flags=re.DOTALL) diff --git a/agentapp.py b/agentapp.py index cdadf58..27750f4 100644 --- a/agentapp.py +++ b/agentapp.py @@ -10,7 +10,6 @@ import time, json, re with open('tools_schema.json', 'r', encoding='utf-8') as f: TOOLS_SCHEMA = json.load(f) - st.set_page_config(page_title="Cowork", layout="wide") from sidercall import SiderLLMSession, LLMSession, ToolClient @@ -25,7 +24,7 @@ def init(): llmclient = init() -from ga import GenericAgentHandler, smart_format +from ga import GenericAgentHandler, smart_format, get_global_memory def get_system_prompt(): if not os.path.exists('memory'): os.makedirs('memory') @@ -33,50 +32,16 @@ def get_system_prompt(): with open('memory/global_mem.txt', 'w', encoding='utf-8') as f: f.write('') if not os.path.exists('memory/global_mem_insight.txt'): with open('memory/global_mem_insight.txt', 'w', encoding='utf-8') as f: f.write('') - with open('sys_prompt.txt', 'r', encoding='utf-8') as f: - prompt = f.read() - try: - with open('memory/global_mem_insight.txt', 'r', encoding='utf-8') as f: - insight = f.read() - prompt += f"\n\n[Global Memory Insight]\n" - prompt += 'IMPORTANT PATHS: ../memory/global_mem.txt (Facts), ../memory/global_mem_insight.txt (Logic), ../ (Your Code Root).\n' - prompt += 'MEM_RULE: Insight is the index of Facts. Sync Insight whenever Facts change. For details, read Facts.\n' - prompt += "EXT: ../memory/ may contain other task-specific memories.\n" - prompt += insight + "\n" - except FileNotFoundError: pass + with open('sys_prompt.txt', 'r', encoding='utf-8') as f: prompt = f.read() + prompt += get_global_memory() return prompt if "last_goal" not in st.session_state: st.session_state.last_goal = "" -def refine_user_goal(raw_query, last_goal): - """通过 LLM 提炼用户真实意图""" - if not last_goal: - return raw_query - - decide_prompt = f""" -用户之前的目标是: "{last_goal}" -用户现在输入了: "{raw_query}" - -请判断: -1. 如果用户提供补充信息、或者是接续之前的任务,请输出合并后的【最终目标】。 -2. 如果用户只是指出之前做法有错而非变更目标,那么请输出原目标不做修改。 -3. 如果用户开启了一个完全不相关的新话题,请直接输出用户现在的输入内容。 - -请直接输出目标描述,不要包含任何多余的文字、解释或标点。 -""" - try: - refined = llmclient.llm_func(decide_prompt).strip() - return refined if refined else raw_query - except: - return raw_query - def agent_backend_stream(raw_query): - #final_goal = refine_user_goal(raw_query, st.session_state.last_goal) - #if final_goal != raw_query: yield f"[Goal Refined] {final_goal}\n" - history = st.session_state.get("last_history", []) - rquery = smart_format(raw_query.replace('\n', ' ')) + rquery = smart_format(raw_query.replace('\n', ' '), max_str_len=200) history.append(f"[USER]: {rquery}") sys_prompt = get_system_prompt() @@ -85,7 +50,6 @@ def agent_backend_stream(raw_query): ret = yield from agent_runner_loop(llmclient, sys_prompt, raw_query, handler, TOOLS_SCHEMA, max_turns=25) - #st.session_state.last_goal = final_goal st.session_state.last_history = handler.history_info return ret diff --git a/ga.py b/ga.py index eb44228..a75fa7d 100644 --- a/ga.py +++ b/ga.py @@ -396,13 +396,13 @@ class GenericAgentHandler(BaseHandler): '''Agent觉得当前任务完成后有重要信息需要记忆时调用此工具。 目前只支持全局记忆,暂不处理过程记忆或特定任务经验。 ''' - next_prompt = '''### [总结提炼经验] 既然你觉得当前任务有重要信息需要记忆,请提取最近一次任务中【事实验证成功且长期有效】的环境事实与用户偏好,更新至全局记忆。 + prompt = '''### [总结提炼经验] 既然你觉得当前任务有重要信息需要记忆,请提取最近一次任务中【事实验证成功且长期有效】的环境事实与用户偏好,更新至全局记忆。 1. 严禁记录任何任务特定中间执行过程或临时变量经验,那是过程记忆不是全局记忆。 2. 若无高价值新事实,那就不更新任何内容。 3. 尽量先查看现有全局记忆形式,仅作少量修改不要影响其余部分。insight也要同步更新全局记忆的短印象来提醒存在性。 -4. 优先使用file_read和file_patch来保证少量修改。''' +4. 优先使用file_read和file_patch来保证少量修改。''' + get_global_memory() yield "[Info] Start distilling good memory for long-term storage.\n" - return StepOutcome({"status": "success"}, next_prompt=next_prompt) + return StepOutcome({"status": "success"}, next_prompt=prompt) def _get_anchor_prompt(self): h_str = "\n".join(self.history_info[-20:]) @@ -411,3 +411,15 @@ class GenericAgentHandler(BaseHandler): if self.plan: prompt += f"\n{self.plan}" if self.focus: prompt += f"\n{self.focus}" return prompt + "\n请继续执行下一步。" + +def get_global_memory(): + prompt = "\n" + try: + with open('memory/global_mem_insight.txt', 'r', encoding='utf-8') as f: insight = f.read() + prompt += f"\n\n[Global Memory Insight]\n" + prompt += 'IMPORTANT PATHS: ../memory/global_mem.txt (Facts), ../memory/global_mem_insight.txt (Logic), ../ (Your Code Root).\n' + prompt += 'MEM_RULE: Insight is the index of Facts. Sync Insight whenever Facts change. For details, read Facts.\n' + prompt += "EXT: ../memory/ may contain other task-specific memories.\n" + prompt += insight + "\n" + except FileNotFoundError: pass + return prompt \ No newline at end of file diff --git a/sidercall.py b/sidercall.py index 5592c14..1418f49 100644 --- a/sidercall.py +++ b/sidercall.py @@ -11,41 +11,65 @@ class SiderLLMSession: def __init__(self, multiturns=6): self._core = Session(cookie=sider_cookie, proxies={'https':'127.0.0.1:2082'}) def ask(self, prompt, model="gemini-3.0-flash"): - if len(prompt) > 30000: prompt = prompt[-29500:] + if len(prompt) > 29000: + print(f"[Warn] Prompt too long ({len(prompt)} chars), truncating.") + prompt = prompt[-29000:] return ''.join(self._core.chat(prompt, model)) class LLMSession: - def __init__(self, api_key=capikey, api_base="http://113.45.39.247:3001/v1", multiturns=6): + def __init__(self, api_key=capikey, api_base="http://113.45.39.247:3001/v1", multiturns=6, context_win=32000): self.api_key = api_key self.api_base = api_base + self.raw_msgs = [] self.messages = [] - self.multiturns = multiturns - - def ask(self, prompt, model="openai/gpt-5.1"): - self.messages.append({"role": "user", "content": prompt}) - if len(self.messages) > self.multiturns: - self.messages = self.messages[-self.multiturns:] - headers = { - "Authorization": f"Bearer {self.api_key}", - "Content-Type": "application/json" - } + self.context_win = context_win + + def raw_ask(self, messages, model, temperature=0.5): + headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} try: response = requests.post( - f"{self.api_base}/chat/completions", - headers=headers, - json={ - "model": model, - "messages": self.messages, - "temperature": 0.5 - }, - timeout=60 - ) + f"{self.api_base}/chat/completions", headers=headers, timeout=60, + json={"model": model, "messages": messages, "temperature": temperature} ) res_json = response.json() content = res_json["choices"][0]["message"]["content"] - self.messages.append({"role": "assistant", "content": content}) return content except Exception as e: return f"Error: {str(e)}" + + def make_messages(self, raw_list, omit_images=True): + messages = [] + for msg in raw_list: + if omit_images and msg['image']: + messages.append({"role": msg['role'], "content": "[Image omitted, if you needed it, ask me]\n" + msg['prompt']}) + elif not omit_images and msg['image']: + messages.append({"role": msg['role'], "content": [ + {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{msg['image']}"}}, + {"type": "text", "text": msg['prompt']} ]}) + else: + messages.append({"role": msg['role'], "content": msg['prompt']}) + return messages + + def summary_history(self, model): + keep = max(2, len(self.raw_msgs)//2) + old, self.raw_msgs = self.raw_msgs[:-keep], self.raw_msgs[-keep:] + if len(old) == 0: old = self.raw_msgs; self.raw_msgs = [] + p = "Summarize prev summary and prev conversations into compact memory (facts/decisions/constraints/open questions). Do NOT restate long schemas. The new summary should less than 1000 tokens.\n" + messages = self.make_messages(old, omit_images=True) + messages += [{"role":"user", "content":p}] + self.summary = self.raw_ask(messages, model, temperature=0.1) + self.raw_msgs.insert(0, {"role":"system", "prompt":"Prev summary:\n"+self.summary, "image":None}) + + def ask(self, prompt, model="openai/gpt-5.1", image_base64=None): + self.raw_msgs.append({"role": "user", "prompt": prompt, "image": image_base64}) + messages = self.make_messages(self.raw_msgs[:-1], omit_images=True) + messages += self.make_messages([self.raw_msgs[-1]], omit_images=False) + total_len = sum(2000 if isinstance(m["content"], list) else len(str(m["content"]))//4 for m in messages) # estimate token count + content = self.raw_ask(messages, model) + if not content.startswith("Error:"): + self.raw_msgs.append({"role": "assistant", "prompt": content, "image": None}) + if total_len > self.context_win: self.summary_history(model) + return content + class MockFunction: def __init__(self, name, arguments): diff --git a/simphtml.py b/simphtml.py index 0f8caf2..18c277e 100644 --- a/simphtml.py +++ b/simphtml.py @@ -1,6 +1,6 @@ from bs4 import BeautifulSoup -js_optHTML = '''function optHTML() { +js_optHTML = r'''function optHTML() { function createEnhancedDOMCopy() { const nodeInfo = new WeakMap(); const ignoreTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'META', 'LINK', 'COLGROUP', 'COL', 'TEMPLATE', 'PARAM', 'SOURCE']; @@ -237,7 +237,7 @@ optHTML()''' -js_findMainList = '''function findMainList(startElement = null) { +js_findMainList = r'''function findMainList(startElement = null) { const containerElement = startElement || document.body; const rect = containerElement.getBoundingClientRect(); const centerX = startElement ? (rect.left + rect.width/2) : (window.innerWidth/2);