feat: --verbose flag, lazy mykeys loading, temperature config support
- agentmain: add --verbose arg for subagent monitoring mode - llmcore: lazy-load mykeys/proxies via module __getattr__ - llmcore: fix auto_make_url regex for trailing slash cases - llmcore: support temperature override from session config - docs: update subagent.md with --verbose usage note
This commit is contained in:
@@ -158,6 +158,7 @@ if __name__ == '__main__':
|
|||||||
parser.add_argument('--reflect', metavar='SCRIPT', help='反射模式:加载监控脚本,check()触发时发任务')
|
parser.add_argument('--reflect', metavar='SCRIPT', help='反射模式:加载监控脚本,check()触发时发任务')
|
||||||
parser.add_argument('--input', help='任务内容')
|
parser.add_argument('--input', help='任务内容')
|
||||||
parser.add_argument('--llm_no', type=int, default=0, help='LLM编号')
|
parser.add_argument('--llm_no', type=int, default=0, help='LLM编号')
|
||||||
|
parser.add_argument('--verbose', action='store_true', help='输出包含工具执行结果(监察模式用)')
|
||||||
parser.add_argument('--bg', action='store_true', help='后台自举: spawn自身去掉--bg, print PID, exit')
|
parser.add_argument('--bg', action='store_true', help='后台自举: spawn自身去掉--bg, print PID, exit')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
agent = GeneraticAgent()
|
agent = GeneraticAgent()
|
||||||
agent.next_llm(args.llm_no)
|
agent.next_llm(args.llm_no)
|
||||||
agent.verbose = False
|
agent.verbose = args.verbose
|
||||||
threading.Thread(target=agent.run, daemon=True).start()
|
threading.Thread(target=agent.run, daemon=True).start()
|
||||||
|
|
||||||
if args.task:
|
if args.task:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Facts(L2): ../memory/global_mem.txt | Code: ../ | SOPs(L3): ../memory/*.md or *.py | META-SOP(L0): ../memory/memory_management_sop.md
|
Facts(L2): ../memory/global_mem.txt | CodeRoot: ../ | SOPs(L3): ../memory/*.md or *.py | META-SOP(L0): ../memory/memory_management_sop.md
|
||||||
L1 Insight是极简索引,L2/L3变更时同步L1,索引必须极简。写记忆前先读META-SOP(L0)。
|
L1 Insight是极简索引,L2/L3变更时同步L1,索引必须极简。写记忆前先读META-SOP(L0)。
|
||||||
|
|
||||||
[CONSTITUTION]
|
[CONSTITUTION]
|
||||||
|
|||||||
30
llmcore.py
30
llmcore.py
@@ -10,9 +10,14 @@ def _load_mykeys():
|
|||||||
if not os.path.exists(p): raise Exception('[ERROR] mykey.py or mykey.json not found, please create one from mykey_template.')
|
if not os.path.exists(p): raise Exception('[ERROR] mykey.py or mykey.json not found, please create one from mykey_template.')
|
||||||
with open(p, encoding='utf-8') as f: return json.load(f)
|
with open(p, encoding='utf-8') as f: return json.load(f)
|
||||||
|
|
||||||
mykeys = _load_mykeys()
|
def __getattr__(name):
|
||||||
proxy = mykeys.get("proxy", 'http://127.0.0.1:2082')
|
if name in ('mykeys', 'proxies'):
|
||||||
proxies = {"http": proxy, "https": proxy} if proxy else None
|
mk = _load_mykeys()
|
||||||
|
proxy = mk.get("proxy", 'http://127.0.0.1:2082')
|
||||||
|
px = {"http": proxy, "https": proxy} if proxy else None
|
||||||
|
globals().update(mykeys=mk, proxies=px)
|
||||||
|
return globals()[name]
|
||||||
|
raise AttributeError(f"module 'llmcore' has no attribute {name}")
|
||||||
|
|
||||||
def compress_history_tags(messages, keep_recent=10, max_len=800, force=False):
|
def compress_history_tags(messages, keep_recent=10, max_len=800, force=False):
|
||||||
"""Compress <thinking>/<tool_use>/<tool_result> tags in older messages to save tokens."""
|
"""Compress <thinking>/<tool_use>/<tool_result> tags in older messages to save tokens."""
|
||||||
@@ -61,8 +66,7 @@ def _sanitize_leading_user_msg(msg):
|
|||||||
if isinstance(c, list): # content 本身也可能是 list[{type:text,text:...}]
|
if isinstance(c, list): # content 本身也可能是 list[{type:text,text:...}]
|
||||||
texts.extend(b.get('text', '') for b in c if isinstance(b, dict))
|
texts.extend(b.get('text', '') for b in c if isinstance(b, dict))
|
||||||
else: texts.append(str(c))
|
else: texts.append(str(c))
|
||||||
elif block.get('type') == 'text':
|
elif block.get('type') == 'text': texts.append(block.get('text', ''))
|
||||||
texts.append(block.get('text', ''))
|
|
||||||
msg['content'] = [{"type": "text", "text": '\n'.join(t for t in texts if t)}]
|
msg['content'] = [{"type": "text", "text": '\n'.join(t for t in texts if t)}]
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
@@ -83,7 +87,8 @@ def trim_messages_history(history, context_win):
|
|||||||
def auto_make_url(base, path):
|
def auto_make_url(base, path):
|
||||||
b, p = base.rstrip('/'), path.strip('/')
|
b, p = base.rstrip('/'), path.strip('/')
|
||||||
if b.endswith('$'): return b[:-1].rstrip('/')
|
if b.endswith('$'): return b[:-1].rstrip('/')
|
||||||
return b if b.endswith(p) else f"{b}/{p}" if re.search(r'/v\d+$', b) else f"{b}/v1/{p}"
|
if b.endswith(p): return b
|
||||||
|
return f"{b}/{p}" if re.search(r'/v\d+(/|$)', b) else f"{b}/v1/{p}"
|
||||||
|
|
||||||
def build_multimodal_content(prompt_text, image_paths):
|
def build_multimodal_content(prompt_text, image_paths):
|
||||||
parts = []
|
parts = []
|
||||||
@@ -439,6 +444,7 @@ class BaseSession:
|
|||||||
if effort and not self.reasoning_effort: print(f"[WARN] Invalid reasoning_effort {effort!r}, ignored.")
|
if effort and not self.reasoning_effort: print(f"[WARN] Invalid reasoning_effort {effort!r}, ignored.")
|
||||||
mode = str(cfg.get('api_mode', 'chat_completions')).strip().lower().replace('-', '_')
|
mode = str(cfg.get('api_mode', 'chat_completions')).strip().lower().replace('-', '_')
|
||||||
self.api_mode = 'responses' if mode in ('responses', 'response') else 'chat_completions'
|
self.api_mode = 'responses' if mode in ('responses', 'response') else 'chat_completions'
|
||||||
|
self.temperature = cfg.get('temperature')
|
||||||
def ask(self, prompt, stream=False):
|
def ask(self, prompt, stream=False):
|
||||||
def _ask_gen():
|
def _ask_gen():
|
||||||
content = ''
|
content = ''
|
||||||
@@ -519,6 +525,7 @@ class NativeClaudeSession(BaseSession):
|
|||||||
def raw_ask(self, messages, temperature=0.5, max_tokens=6144):
|
def raw_ask(self, messages, temperature=0.5, max_tokens=6144):
|
||||||
messages = _fix_messages(messages)
|
messages = _fix_messages(messages)
|
||||||
model = self.default_model
|
model = self.default_model
|
||||||
|
if self.temperature is not None: temperature = self.temperature
|
||||||
beta_parts = ["claude-code-20250219", "interleaved-thinking-2025-05-14", "redact-thinking-2026-02-12", "prompt-caching-scope-2026-01-05"]
|
beta_parts = ["claude-code-20250219", "interleaved-thinking-2025-05-14", "redact-thinking-2026-02-12", "prompt-caching-scope-2026-01-05"]
|
||||||
if "[1m]" in model.lower():
|
if "[1m]" in model.lower():
|
||||||
beta_parts.insert(1, "context-1m-2025-08-07"); model = model.replace("[1m]", "").replace("[1M]", "")
|
beta_parts.insert(1, "context-1m-2025-08-07"); model = model.replace("[1m]", "").replace("[1M]", "")
|
||||||
@@ -553,7 +560,6 @@ class NativeClaudeSession(BaseSession):
|
|||||||
self.history.append(msg)
|
self.history.append(msg)
|
||||||
trim_messages_history(self.history, self.context_win)
|
trim_messages_history(self.history, self.context_win)
|
||||||
messages = [{"role": m["role"], "content": list(m["content"])} for m in self.history]
|
messages = [{"role": m["role"], "content": list(m["content"])} for m in self.history]
|
||||||
|
|
||||||
content_blocks = None
|
content_blocks = None
|
||||||
gen = self.raw_ask(messages)
|
gen = self.raw_ask(messages)
|
||||||
try:
|
try:
|
||||||
@@ -580,8 +586,8 @@ class NativeOAISession(NativeClaudeSession):
|
|||||||
"""OpenAI streaming. yields text chunks, generator return = list[content_block]"""
|
"""OpenAI streaming. yields text chunks, generator return = list[content_block]"""
|
||||||
msgs = ([{"role": "system", "content": self.system}] if self.system else []) + _msgs_claude2oai(messages)
|
msgs = ([{"role": "system", "content": self.system}] if self.system else []) + _msgs_claude2oai(messages)
|
||||||
return (yield from _openai_stream(self.api_base, self.api_key, msgs, self.default_model, self.api_mode,
|
return (yield from _openai_stream(self.api_base, self.api_key, msgs, self.default_model, self.api_mode,
|
||||||
temperature=temperature, max_tokens=max_tokens, tools=self.tools,
|
temperature=temperature, max_tokens=max_tokens,
|
||||||
reasoning_effort=self.reasoning_effort,
|
tools=self.tools, reasoning_effort=self.reasoning_effort,
|
||||||
max_retries=self.max_retries, connect_timeout=self.connect_timeout,
|
max_retries=self.max_retries, connect_timeout=self.connect_timeout,
|
||||||
read_timeout=self.read_timeout, proxies=self.proxies))
|
read_timeout=self.read_timeout, proxies=self.proxies))
|
||||||
|
|
||||||
@@ -591,10 +597,8 @@ def openai_tools_to_claude(tools):
|
|||||||
for t in tools:
|
for t in tools:
|
||||||
if 'input_schema' in t: result.append(t); continue # 已是claude格式
|
if 'input_schema' in t: result.append(t); continue # 已是claude格式
|
||||||
fn = t.get('function', t)
|
fn = t.get('function', t)
|
||||||
result.append({
|
result.append({'name': fn['name'], 'description': fn.get('description', ''),
|
||||||
'name': fn['name'], 'description': fn.get('description', ''),
|
'input_schema': fn.get('parameters', {'type': 'object', 'properties': {}})})
|
||||||
'input_schema': fn.get('parameters', {'type': 'object', 'properties': {}})
|
|
||||||
})
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
- 通信:output.txt(append,`[ROUND END]`=轮完成) → 写reply.txt继续 → 不写10min退出。reply后输出为output1/2/3.txt(同格式)
|
- 通信:output.txt(append,`[ROUND END]`=轮完成) → 写reply.txt继续 → 不写10min退出。reply后输出为output1/2/3.txt(同格式)
|
||||||
- 干预文件:`_stop`(当轮结束退出) | `_keyinfo`(注入working memory) | `_intervene`(追加指令)
|
- 干预文件:`_stop`(当轮结束退出) | `_keyinfo`(注入working memory) | `_intervene`(追加指令)
|
||||||
- **主agent空闲时应读output观察进度,必要时用干预文件纠偏,禁止无脑长时间sleep轮询**
|
- **主agent空闲时应读output观察进度,必要时用干预文件纠偏,禁止无脑长时间sleep轮询**
|
||||||
|
- 监察模式启动时加`--verbose`,output将包含工具执行结果,主agent可直接审查原始数据而非仅信任摘要
|
||||||
|
|
||||||
## 场景1:测试模式 - 行为验证
|
## 场景1:测试模式 - 行为验证
|
||||||
**用途**:观察agent真实行为,修正RULES/L2/L3/SOP
|
**用途**:观察agent真实行为,修正RULES/L2/L3/SOP
|
||||||
|
|||||||
Reference in New Issue
Block a user