feat: i18n support - auto-detect system language for zh/en prompts
This commit is contained in:
@@ -70,7 +70,7 @@ def agent_runner_loop(client, system_prompt, user_input, handler, tools_schema,
|
||||
tool_name, args, tid = tc['tool_name'], tc['args'], tc.get('id', '')
|
||||
if tool_name == 'no_tool': pass
|
||||
else:
|
||||
if verbose: yield f"🛠️ 正在调用工具: `{tool_name}` 📥参数:\n````text\n{get_pretty_json(args)}\n````\n"
|
||||
if verbose: yield f"🛠️ Tool: `{tool_name}` 📥 args:\n````text\n{get_pretty_json(args)}\n````\n"
|
||||
else: yield f"🛠️ {tool_name}({_compact_tool_args(tool_name, args)})\n\n\n"
|
||||
handler.current_turn = turn
|
||||
gen = handler.dispatch(tool_name, args, response, index=ii)
|
||||
@@ -104,8 +104,8 @@ def _clean_content(text):
|
||||
def _shrink_code(m):
|
||||
lines = m.group(0).split('\n')
|
||||
lang = lines[0].replace('```','').strip()
|
||||
body = [l for l in lines[1:-1] if l.strip()] # 去掉```行和空行
|
||||
if len(body) <= 6: return m.group(0) # 短代码保留
|
||||
body = [l for l in lines[1:-1] if l.strip()]
|
||||
if len(body) <= 6: return m.group(0)
|
||||
preview = '\n'.join(body[:5])
|
||||
return f'```{lang}\n{preview}\n ... ({len(body)} lines)\n```'
|
||||
text = re.sub(r'```[\s\S]*?```', _shrink_code, text)
|
||||
@@ -115,7 +115,7 @@ def _clean_content(text):
|
||||
|
||||
def _compact_tool_args(name, args):
|
||||
a = {k: v for k, v in args.items() if k != '_index'}
|
||||
for k in ('path',): # 只缩短路径
|
||||
for k in ('path',):
|
||||
if k in a: a[k] = os.path.basename(a[k])
|
||||
if name == 'update_working_checkpoint': s = a.get('key_info', ''); return (s[:60]+'...') if len(s)>60 else s
|
||||
s = json.dumps(a, ensure_ascii=False); return (s[:120]+'...') if len(s)>120 else s
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os, sys, threading, queue, time, json, re, random
|
||||
import os, sys, threading, queue, time, json, re, random, locale
|
||||
os.environ.setdefault('GA_LANG', 'zh' if any(k in (locale.getlocale()[0] or '').lower() for k in ('zh', 'chinese')) else 'en')
|
||||
if sys.stdout is None: sys.stdout = open(os.devnull, "w")
|
||||
elif hasattr(sys.stdout, 'reconfigure'): sys.stdout.reconfigure(errors='replace')
|
||||
if sys.stderr is None: sys.stderr = open(os.devnull, "w")
|
||||
@@ -32,7 +33,8 @@ if not os.path.exists(cdp_cfg):
|
||||
except Exception as e: print(f'[WARN] CDP config init failed: {e} — advanced web features (tmwebdriver) will be unavailable.')
|
||||
|
||||
def get_system_prompt():
|
||||
with open(os.path.join(script_dir, 'assets/sys_prompt.txt'), 'r', encoding='utf-8') as f: prompt = f.read()
|
||||
suffix = '_en' if os.environ.get('GA_LANG', '') == 'en' else ''
|
||||
with open(os.path.join(script_dir, f'assets/sys_prompt{suffix}.txt'), 'r', encoding='utf-8') as f: prompt = f.read()
|
||||
prompt += f"\nToday: {time.strftime('%Y-%m-%d %a')}\n"
|
||||
prompt += get_global_memory()
|
||||
return prompt
|
||||
|
||||
9
assets/insight_fixed_structure_en.txt
Normal file
9
assets/insight_fixed_structure_en.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Facts(L2): ../memory/global_mem.txt | CodeRoot: ../ | SOPs(L3): ../memory/*.md or *.py | META-SOP(L0): ../memory/memory_management_sop.md
|
||||
L1 Insight is a minimal index; sync L1 when L2/L3 changes; keep index minimal. Read META-SOP(L0) before writing any memory.
|
||||
|
||||
[CONSTITUTION]
|
||||
1. Ask before modifying own source code; free to experiment within ./; installing packages and portable tools allowed
|
||||
2. Check memory before decisions; always use existing SOPs/utils; revisit SOPs on repeated failures; never assert without evidence
|
||||
3. Execute step by step, control granularity, limit blast radius; request intervention after 3 failures
|
||||
4. Key/secret files: reference only, never read or move
|
||||
5. Read META-SOP to verify before writing any memory; files under memory/ must be patched only (unless creating new)
|
||||
7
assets/sys_prompt_en.txt
Normal file
7
assets/sys_prompt_en.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Role: Physical-Level Omnipotent Executor
|
||||
You have full physical access: file I/O, script execution, browser JS injection, and system-level intervention. Never deflect with "can't do it" — don't speculate, use tools to probe.
|
||||
Use English to thinking, summary and reply.
|
||||
## Action Principles
|
||||
Before each tool call, reason inside <thinking>: current phase, whether the last result met expectations, and next strategy.
|
||||
- Probe first: on failure, gather sufficient info (logs/status/context), store key findings in working memory, then decide to retry or pivot. Ask the user before irreversible operations.
|
||||
- Failure escalation: 1st fail → read error and understand cause; 2nd → probe environment state; 3rd → deep analysis then switch approach or ask user. Never repeat an action without new information.
|
||||
@@ -171,7 +171,7 @@ _js_ime_fix = ("" if os.name == 'nt' else
|
||||
"f();new MutationObserver(f).observe(d.body,{childList:1,subtree:1})}()")
|
||||
_embed_html(f'<script>{_js_scroll_fix};{_js_ime_fix}</script>', height=0)
|
||||
|
||||
if prompt := st.chat_input("请输入指令"):
|
||||
if prompt := st.chat_input("any task?"):
|
||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
||||
if hasattr(agent, '_pet_req') and not prompt.startswith('/'): agent._pet_req('state=walk')
|
||||
with st.chat_message("user"): st.markdown(prompt)
|
||||
|
||||
5
ga.py
5
ga.py
@@ -566,9 +566,10 @@ def get_global_memory():
|
||||
prompt = "\n"
|
||||
try:
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
suffix = '_en' if os.environ.get('GA_LANG', '') == 'en' else ''
|
||||
with open(os.path.join(script_dir, 'memory/global_mem_insight.txt'), 'r', encoding='utf-8', errors='replace') as f: insight = f.read()
|
||||
with open(os.path.join(script_dir, 'assets/insight_fixed_structure.txt'), 'r', encoding='utf-8') as f: structure = f.read()
|
||||
prompt += f'cwd = {os.path.join(script_dir, "temp")} (用./引用)\n'
|
||||
with open(os.path.join(script_dir, f'assets/insight_fixed_structure{suffix}.txt'), 'r', encoding='utf-8') as f: structure = f.read()
|
||||
prompt += f'cwd = {os.path.join(script_dir, "temp")} (./)\n'
|
||||
prompt += f"\n[Memory] (../memory)\n"
|
||||
prompt += structure + '\n../memory/global_mem_insight.txt:\n'
|
||||
prompt += insight + "\n"
|
||||
|
||||
35
llmcore.py
35
llmcore.py
@@ -668,19 +668,26 @@ class ToolClient:
|
||||
tool_instruction = ""
|
||||
if not tools: return tool_instruction
|
||||
tools_json = json.dumps(tools, ensure_ascii=False, separators=(',', ':'))
|
||||
_en = os.environ.get('GA_LANG') == 'en'
|
||||
if _en:
|
||||
tool_instruction = f"""
|
||||
### Interaction Protocol (must follow strictly, always in effect)
|
||||
Follow these steps to think and act:
|
||||
1. **Think**: Analyze the current situation and strategy inside `<thinking>` tags.
|
||||
2. **Summarize**: Output a minimal one-line (<30 words) physical snapshot in `<summary>`: new info from last tool result + current tool call intent. This goes into long-term working memory. Must contain real information, no filler.
|
||||
3. **Act**: If you need to call tools, output one or more **<tool_use> blocks** after your reply, then stop.
|
||||
"""
|
||||
else:
|
||||
tool_instruction = f"""
|
||||
### 交互协议 (必须严格遵守,持续有效)
|
||||
请按照以下步骤思考并行动:
|
||||
1. **思考**: 在 `<thinking>` 标签中先进行思考,分析现状和策略。
|
||||
2. **总结**: 在 `<summary>` 中输出*极为简短*的高度概括的单行(<30字)物理快照,包括上次工具调用结果产生的新信息+本次工具调用意图。此内容将进入长期工作记忆,记录关键信息,严禁输出无实际信息增量的描述。
|
||||
3. **行动**: 如需调用工具,请在回复正文之后输出一个(或多个)**<tool_use>块**,然后结束。
|
||||
格式: ```<tool_use>{{"name": "工具名", "arguments": {{参数}}}}</tool_use>```
|
||||
|
||||
### 可用工具库(已挂载,持续有效)
|
||||
{tools_json}
|
||||
"""
|
||||
tool_instruction += f'\nFormat: ```<tool_use>{{"name": "tool_name", "arguments": {{...}}}}</tool_use>```\n\n### Tools (mounted, always in effect):\n{tools_json}\n'
|
||||
if self.auto_save_tokens and self.last_tools == tools_json:
|
||||
tool_instruction = "\n### 工具库状态:持续有效(code_run/file_read等),**可正常调用**。调用协议沿用。\n"
|
||||
tool_instruction = "\n### Tools: still active, **ready to call**. Protocol unchanged.\n" if _en else "\n### 工具库状态:持续有效(code_run/file_read等),**可正常调用**。调用协议沿用。\n"
|
||||
else: self.total_cd_tokens = 0
|
||||
self.last_tools = tools_json
|
||||
return tool_instruction
|
||||
@@ -851,21 +858,31 @@ class MixinSession:
|
||||
time.sleep(delay)
|
||||
else: print(f'[MixinSession] {last_chunk[:80]}, retry {attempt+1}/{self._retries} (s{idx}→s{nxt})')
|
||||
|
||||
class NativeToolClient:
|
||||
THINKING_PROMPT = """
|
||||
THINKING_PROMPT_ZH = """
|
||||
### 行动规范(持续有效)
|
||||
每次回复请遵循:
|
||||
1. 在 <thinking></thinking> 标签中先分析现状和策略
|
||||
2. 在 <summary></summary> 中输出极简单行(<30字)物理快照:上次结果新信息+本次意图。此内容进入长期工作记忆。
|
||||
3. 然后才能输出工具调用
|
||||
""".strip()
|
||||
THINKING_PROMPT_EN = """
|
||||
### Action Protocol (always in effect)
|
||||
For every reply, follow these steps:
|
||||
1. Analyze the current situation and strategy inside <thinking></thinking>
|
||||
2. Output a minimal one-line (<30 words) physical snapshot in <summary></summary>: new info from last result + current intent. This goes into long-term working memory.
|
||||
3. Then output tool calls
|
||||
""".strip()
|
||||
|
||||
class NativeToolClient:
|
||||
@staticmethod
|
||||
def _thinking_prompt(): return THINKING_PROMPT_EN if os.environ.get('GA_LANG') == 'en' else THINKING_PROMPT_ZH
|
||||
def __init__(self, backend):
|
||||
self.backend = backend
|
||||
self.backend.system = self.THINKING_PROMPT
|
||||
self.backend.system = self._thinking_prompt()
|
||||
self.name = self.backend.name
|
||||
self._pending_tool_ids = []
|
||||
def set_system(self, extra_system):
|
||||
combined = f"{extra_system}\n\n{self.THINKING_PROMPT}" if extra_system else self.THINKING_PROMPT
|
||||
combined = f"{extra_system}\n\n{self._thinking_prompt()}" if extra_system else self._thinking_prompt()
|
||||
if combined != self.backend.system: print(f"[Debug] Updated system prompt, length {len(combined)} chars.")
|
||||
self.backend.system = combined
|
||||
def chat(self, messages, tools=None):
|
||||
|
||||
Reference in New Issue
Block a user