myagent-ai 1.15.2 → 1.15.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/base.py +1 -0
- package/agents/main_agent.py +77 -10
- package/core/context_builder.py +27 -17
- package/core/deps_checker.py +5 -0
- package/core/llm.py +4 -0
- package/core/output_parser.py +6 -0
- package/departments/manager.py +4 -0
- package/main.py +1 -0
- package/package.json +1 -1
- package/start.js +1 -0
- package/web/api_server.py +109 -20
- package/web/ui/chat/chat.css +12 -3
- package/web/ui/chat/chat_main.js +131 -16
- package/web/ui/chat/flow_engine.js +134 -20
- package/web/ui/index.html +18 -9
package/agents/base.py
CHANGED
package/agents/main_agent.py
CHANGED
|
@@ -57,6 +57,7 @@ class MainAgent(BaseAgent):
|
|
|
57
57
|
<finish>true/false,是否结束循环调用llm。如"askuser"为非空,则"finish"输出true。否则,根据"context"判断任务是否已完成,是否结束llm回调</finish>
|
|
58
58
|
<finish_reason>当 finish=true 时必填,详细说明为什么现在结束任务(如:任务已完成/需要用户补充信息/信息不足无法继续等)。finish=false 时为空。</finish_reason>
|
|
59
59
|
<next_step>当 finish=false 时必填,描述下一步计划做什么(简洁明了,1-2句话)。finish=true 时为空。</next_step>
|
|
60
|
+
<mainsubject>为当前对话生成一个简短的标题(6个字以内),概括对话主题。仅在对话刚开始(前几轮)时需要输出,已有标题的对话此标签留空即可。要求简洁精炼,例如"Python安装"、"请假流程"、"翻译助手"等。</mainsubject>
|
|
60
61
|
|
|
61
62
|
</output>
|
|
62
63
|
|
|
@@ -77,7 +78,8 @@ class MainAgent(BaseAgent):
|
|
|
77
78
|
14. <finish>: 当任务已完成或需要等待用户回应时为 true;否则为 false 继续执行
|
|
78
79
|
15. <finish_reason>: **finish=true 时必须填写**,详细说明结束原因(任务完成/等待用户/信息不足/无法处理等)
|
|
79
80
|
16. <next_step>: **finish=false 时必须填写**,描述下一步计划做什么,要求简洁明确(1-2句话)
|
|
80
|
-
17.
|
|
81
|
+
17. <mainsubject>: 为当前对话生成6字以内的简短标题,概括对话主题。仅在对话刚开始的前几轮需要输出,已有标题后留空
|
|
82
|
+
18. 使用中文输出所有内容
|
|
81
83
|
|
|
82
84
|
## 上下文中的记忆系统说明
|
|
83
85
|
- <automemory>: 系统自动根据你通过 <remember> 保存的记忆和当前用户输入,搜索出的 top10 相关记忆。这些是你过去主动记住的内容(包含时间信息),可供参考。
|
|
@@ -109,14 +111,19 @@ class MainAgent(BaseAgent):
|
|
|
109
111
|
# 活跃会话上下文追踪(用于消息注入)
|
|
110
112
|
self.active_contexts: Dict[str, AgentContext] = {}
|
|
111
113
|
|
|
112
|
-
def init_context_builder(self, memory_manager=None, skill_registry=None, knowledge_base_dir=None):
|
|
114
|
+
def init_context_builder(self, memory_manager=None, skill_registry=None, knowledge_base_dir=None, context_window=None):
|
|
113
115
|
"""初始化 Context Builder(在系统启动后调用,注入依赖)"""
|
|
116
|
+
if context_window is None and self.llm:
|
|
117
|
+
context_window = getattr(self.llm, 'context_window', 128000)
|
|
118
|
+
if context_window is None:
|
|
119
|
+
context_window = 128000
|
|
114
120
|
self.context_builder = ContextBuilder(
|
|
115
121
|
memory_manager=memory_manager,
|
|
116
122
|
skill_registry=skill_registry,
|
|
117
123
|
knowledge_base_dir=knowledge_base_dir,
|
|
124
|
+
context_window=context_window,
|
|
118
125
|
)
|
|
119
|
-
logger.info("Context Builder 已初始化" + (f" (知识库: {knowledge_base_dir})" if knowledge_base_dir else ""))
|
|
126
|
+
logger.info(f"Context Builder 已初始化 (context_window={context_window})" + (f" (知识库: {knowledge_base_dir})" if knowledge_base_dir else ""))
|
|
120
127
|
|
|
121
128
|
def _add_exec_event(self, event_type: str, data: Dict):
|
|
122
129
|
"""记录一个执行事件(供前端展示)"""
|
|
@@ -288,14 +295,24 @@ class MainAgent(BaseAgent):
|
|
|
288
295
|
Returns:
|
|
289
296
|
True 表示成功存储了新知识,False 表示跳过(重复)或失败
|
|
290
297
|
"""
|
|
291
|
-
if not self.context_builder
|
|
298
|
+
if not self.context_builder:
|
|
292
299
|
logger.debug(f"[{task_id}] 知识库未配置,跳过 knowledge 存储")
|
|
293
300
|
return False
|
|
294
301
|
|
|
295
302
|
from datetime import datetime
|
|
296
303
|
from pathlib import Path
|
|
297
304
|
|
|
298
|
-
|
|
305
|
+
# 优先写入 Agent 专属知识库,其次回退到组织知识库
|
|
306
|
+
kb_dir = None
|
|
307
|
+
if self.context_builder.agent_knowledge_dir:
|
|
308
|
+
kb_dir = Path(self.context_builder.agent_knowledge_dir)
|
|
309
|
+
elif self.context_builder.knowledge_base_dir:
|
|
310
|
+
kb_dir = Path(self.context_builder.knowledge_base_dir)
|
|
311
|
+
|
|
312
|
+
if not kb_dir:
|
|
313
|
+
logger.debug(f"[{task_id}] 知识库目录未配置,跳过 knowledge 存储")
|
|
314
|
+
return False
|
|
315
|
+
|
|
299
316
|
auto_kb_dir = kb_dir / "auto_knowledge"
|
|
300
317
|
auto_kb_dir.mkdir(parents=True, exist_ok=True)
|
|
301
318
|
|
|
@@ -366,7 +383,7 @@ class MainAgent(BaseAgent):
|
|
|
366
383
|
f.write(f"\n## {now_str}\n")
|
|
367
384
|
f.write(content.strip() + "\n")
|
|
368
385
|
logger.info(
|
|
369
|
-
f"[{task_id}] 知识已存入知识库: {kb_file
|
|
386
|
+
f"[{task_id}] 知识已存入知识库: {kb_file} "
|
|
370
387
|
f"({len(content)} 字符, {len(content.strip().split(chr(10)))} 条)"
|
|
371
388
|
)
|
|
372
389
|
return True
|
|
@@ -468,6 +485,12 @@ class MainAgent(BaseAgent):
|
|
|
468
485
|
except Exception as e:
|
|
469
486
|
logger.warning(f"[{task_id}] 加载历史对话失败: {e}")
|
|
470
487
|
|
|
488
|
+
# [v1.15.8] 计算历史用户消息数(用于判断是否需要自动命名会话)
|
|
489
|
+
_history_user_msg_count = sum(
|
|
490
|
+
1 for m in conversation_history if getattr(m, 'role', '') == 'user'
|
|
491
|
+
and getattr(m, 'metadata', {}).get('key', '') not in ('llm_output', 'conversation_insight')
|
|
492
|
+
)
|
|
493
|
+
|
|
471
494
|
# 保存用户消息到会话记忆
|
|
472
495
|
if self.memory:
|
|
473
496
|
self.memory.add_session(
|
|
@@ -493,6 +516,7 @@ class MainAgent(BaseAgent):
|
|
|
493
516
|
while self._iteration_count < max_iter:
|
|
494
517
|
self._iteration_count += 1
|
|
495
518
|
logger.info(f"[{task_id}] V2 迭代 {self._iteration_count}/{max_iter}")
|
|
519
|
+
_emitted_reasoning_this_iter = False # [v1.15.8] 本轮是否已发送过 v2_reasoning 事件(防 TTS 重复)
|
|
496
520
|
|
|
497
521
|
# ── 检查配置热加载广播 ──
|
|
498
522
|
if self.config_broadcaster:
|
|
@@ -538,7 +562,7 @@ class MainAgent(BaseAgent):
|
|
|
538
562
|
+ self.SYSTEM_PROMPT.split("\n", 1)[1]
|
|
539
563
|
)
|
|
540
564
|
system_content = _prompt_with_placeholder.replace(_CONTEXT_PLACEHOLDER, context_xml)
|
|
541
|
-
# Step 3:
|
|
565
|
+
# Step 3: 构建 LLM 消息(必须包含 role=user,否则 OpenAI 兼容 API 返回 400)
|
|
542
566
|
messages = [Message(role="system", content=system_content)]
|
|
543
567
|
|
|
544
568
|
if all_tool_outputs:
|
|
@@ -547,6 +571,11 @@ class MainAgent(BaseAgent):
|
|
|
547
571
|
content=f"[上一轮工具执行结果汇总]\n{truncate_str(all_tool_outputs, 30000)}"
|
|
548
572
|
))
|
|
549
573
|
all_tool_outputs = ""
|
|
574
|
+
else:
|
|
575
|
+
messages.append(Message(
|
|
576
|
+
role="user",
|
|
577
|
+
content=context.user_message or "请处理上述上下文。"
|
|
578
|
+
))
|
|
550
579
|
|
|
551
580
|
if stream_response and self.llm:
|
|
552
581
|
response = await self._call_llm_stream(
|
|
@@ -561,6 +590,12 @@ class MainAgent(BaseAgent):
|
|
|
561
590
|
error_msg = f"LLM 调用失败: {response.error}"
|
|
562
591
|
context.working_memory["final_response"] = error_msg
|
|
563
592
|
await self._emit_v2_event("v2_reasoning", {"content": error_msg}, stream_callback)
|
|
593
|
+
if self.memory:
|
|
594
|
+
self.memory.add_session(
|
|
595
|
+
session_id=context.session_id,
|
|
596
|
+
role="assistant",
|
|
597
|
+
content=error_msg,
|
|
598
|
+
)
|
|
564
599
|
break
|
|
565
600
|
|
|
566
601
|
llm_raw = response.content
|
|
@@ -575,6 +610,15 @@ class MainAgent(BaseAgent):
|
|
|
575
610
|
key="llm_output",
|
|
576
611
|
importance=0.3,
|
|
577
612
|
)
|
|
613
|
+
# 保存推理模型的思考过程(key=reasoning 供前端刷新后展示)
|
|
614
|
+
if response.reasoning and response.reasoning.strip():
|
|
615
|
+
self.memory.add_session(
|
|
616
|
+
session_id=context.session_id,
|
|
617
|
+
role="assistant",
|
|
618
|
+
content=response.reasoning.strip(),
|
|
619
|
+
key="reasoning",
|
|
620
|
+
importance=0.2,
|
|
621
|
+
)
|
|
578
622
|
|
|
579
623
|
# Step 4: 解析结构化输出
|
|
580
624
|
parsed = parse_output(llm_raw)
|
|
@@ -661,6 +705,7 @@ class MainAgent(BaseAgent):
|
|
|
661
705
|
logger.debug(f"[{task_id}] 模型回复用户: {response_text[:100]}")
|
|
662
706
|
context.working_memory["model_response"] = response_text
|
|
663
707
|
_v2_reasoning_collected.append(response_text)
|
|
708
|
+
_emitted_reasoning_this_iter = True
|
|
664
709
|
await self._emit_v2_event(
|
|
665
710
|
"v2_reasoning",
|
|
666
711
|
{"content": response_text},
|
|
@@ -746,6 +791,21 @@ class MainAgent(BaseAgent):
|
|
|
746
791
|
except Exception as e:
|
|
747
792
|
logger.warning(f"[{task_id}] 存入知识库失败: {e}")
|
|
748
793
|
|
|
794
|
+
# Step 6.6: [v1.15.8] 处理 mainsubject — 自动命名会话标题
|
|
795
|
+
if parsed.mainsubject and _history_user_msg_count < 4:
|
|
796
|
+
try:
|
|
797
|
+
subject = parsed.mainsubject.strip()[:20] # 安全截断
|
|
798
|
+
if self.memory:
|
|
799
|
+
self.memory.rename_session(context.session_id, subject)
|
|
800
|
+
logger.info(f"[{task_id}] 会话自动命名: {subject}")
|
|
801
|
+
await self._emit_v2_event(
|
|
802
|
+
"v2_session_rename",
|
|
803
|
+
{"session_id": context.session_id, "name": subject},
|
|
804
|
+
stream_callback,
|
|
805
|
+
)
|
|
806
|
+
except Exception as e:
|
|
807
|
+
logger.warning(f"[{task_id}] 会话自动命名失败: {e}")
|
|
808
|
+
|
|
749
809
|
# Step 7: 处理 recall — 记录下一轮需要检索的记忆内容
|
|
750
810
|
if parsed.recall:
|
|
751
811
|
recall_content = parsed.recall
|
|
@@ -795,7 +855,9 @@ class MainAgent(BaseAgent):
|
|
|
795
855
|
before, after = extract_surrounding_text(llm_raw)
|
|
796
856
|
final_text = (before + "\n" + after).strip() if (before.strip() or after.strip()) else "任务已完成。"
|
|
797
857
|
context.working_memory["final_response"] = final_text
|
|
798
|
-
|
|
858
|
+
# [v1.15.8] 跳过已发送的 reasoning 文本,避免 TTS 重复播报
|
|
859
|
+
if not _emitted_reasoning_this_iter:
|
|
860
|
+
await self._emit_v2_event("v2_reasoning", {"content": truncate_str(final_text, 3000)}, stream_callback)
|
|
799
861
|
if self.memory:
|
|
800
862
|
self.memory.add_session(
|
|
801
863
|
session_id=context.session_id,
|
|
@@ -824,6 +886,7 @@ class MainAgent(BaseAgent):
|
|
|
824
886
|
# 发送 beforecalltext 作为显示文本
|
|
825
887
|
if before_call:
|
|
826
888
|
_v2_reasoning_collected.append(before_call)
|
|
889
|
+
_emitted_reasoning_this_iter = True
|
|
827
890
|
await self._emit_v2_event(
|
|
828
891
|
"v2_reasoning",
|
|
829
892
|
{"content": before_call},
|
|
@@ -1069,7 +1132,9 @@ class MainAgent(BaseAgent):
|
|
|
1069
1132
|
if current_task_plan:
|
|
1070
1133
|
final_text += f"\n\n任务计划:\n{current_task_plan}"
|
|
1071
1134
|
context.working_memory["final_response"] = final_text
|
|
1072
|
-
|
|
1135
|
+
# [v1.15.8] 跳过已发送的 reasoning 文本,避免 TTS 重复播报
|
|
1136
|
+
if not _emitted_reasoning_this_iter:
|
|
1137
|
+
await self._emit_v2_event("v2_reasoning", {"content": truncate_str(final_text, 3000)}, stream_callback)
|
|
1073
1138
|
if self.memory:
|
|
1074
1139
|
self.memory.add_session(
|
|
1075
1140
|
session_id=context.session_id,
|
|
@@ -1096,7 +1161,9 @@ class MainAgent(BaseAgent):
|
|
|
1096
1161
|
if current_task_plan:
|
|
1097
1162
|
final_text += f"\n\n任务计划:\n{current_task_plan}"
|
|
1098
1163
|
context.working_memory["final_response"] = final_text
|
|
1099
|
-
|
|
1164
|
+
# [v1.15.8] 跳过已发送的 reasoning 文本,避免 TTS 重复播报
|
|
1165
|
+
if not _emitted_reasoning_this_iter:
|
|
1166
|
+
await self._emit_v2_event("v2_reasoning", {"content": truncate_str(final_text, 3000)}, stream_callback)
|
|
1100
1167
|
if self.memory:
|
|
1101
1168
|
self.memory.add_session(
|
|
1102
1169
|
session_id=context.session_id,
|
package/core/context_builder.py
CHANGED
|
@@ -47,17 +47,23 @@ _rag_cache: dict = {} # {abs_kb_dir: {"rag": KnowledgeRAG, "mtime": str}}
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def _compute_dir_mtime(dir_path: str) -> str:
|
|
50
|
-
"""计算目录下所有支持文件的修改时间摘要(用于脏检测)
|
|
50
|
+
"""计算目录下所有支持文件的修改时间摘要(用于脏检测)
|
|
51
|
+
|
|
52
|
+
[v1.15.7] 修复: 使用 os.walk 递归扫描子目录(如 auto_knowledge/),
|
|
53
|
+
之前仅扫描顶层目录,导致子目录新增文件无法被脏检测发现,
|
|
54
|
+
造成 RAG 缓存使用过期索引。
|
|
55
|
+
"""
|
|
51
56
|
import os
|
|
52
57
|
_KB_EXTS = {".md", ".txt", ".json", ".csv", ".py", ".js", ".html"}
|
|
53
58
|
mtimes = []
|
|
54
59
|
try:
|
|
55
|
-
for
|
|
56
|
-
|
|
57
|
-
if os.path.isfile(fp):
|
|
60
|
+
for root, dirs, files in os.walk(dir_path):
|
|
61
|
+
for f in sorted(files):
|
|
58
62
|
ext = os.path.splitext(f)[1].lower()
|
|
59
63
|
if ext in _KB_EXTS:
|
|
60
|
-
|
|
64
|
+
fp = os.path.join(root, f)
|
|
65
|
+
rel = os.path.relpath(fp, dir_path)
|
|
66
|
+
mtimes.append(f"{rel}:{os.path.getmtime(fp)}")
|
|
61
67
|
except OSError:
|
|
62
68
|
pass
|
|
63
69
|
return "|".join(sorted(mtimes))
|
|
@@ -92,11 +98,13 @@ class ContextBuilder:
|
|
|
92
98
|
skill_registry: Optional["SkillRegistry"] = None,
|
|
93
99
|
knowledge_base_dir: Optional[str] = None,
|
|
94
100
|
max_dialog_chars: int = 20000,
|
|
101
|
+
context_window: int = 128000,
|
|
95
102
|
) -> None:
|
|
96
103
|
self.memory_manager = memory_manager
|
|
97
104
|
self.skill_registry = skill_registry
|
|
98
105
|
self.knowledge_base_dir = knowledge_base_dir
|
|
99
106
|
self.max_dialog_chars = max_dialog_chars
|
|
107
|
+
self.context_window = context_window
|
|
100
108
|
# Agent 专属知识库目录(由调用方动态设置,优先于组织知识库)
|
|
101
109
|
self.agent_knowledge_dir: Optional[str] = None
|
|
102
110
|
|
|
@@ -155,7 +163,9 @@ class ContextBuilder:
|
|
|
155
163
|
self._build_whomi(agent_name, agent_description, agent_override_prompt),
|
|
156
164
|
self._build_memory(query, session_id, recall),
|
|
157
165
|
self._build_knowledge(kb_query),
|
|
158
|
-
|
|
166
|
+
# [v1.15.6] 注释掉聊天历史注入 — 仅保留用户最新消息 + 工具回调,
|
|
167
|
+
# 其他上下文由系统记忆(automemory/recall_memory)自行处理
|
|
168
|
+
# self._build_recent_dialog(conversation_history, self.max_dialog_chars, session_id),
|
|
159
169
|
self._build_user_input(user_typed_text, user_voice_text),
|
|
160
170
|
self._build_task_plan(task_plan),
|
|
161
171
|
self._build_tools(self.skill_registry),
|
|
@@ -332,9 +342,9 @@ class ContextBuilder:
|
|
|
332
342
|
"""
|
|
333
343
|
构建 <knowledge> 段落 —— 知识库 RAG 检索结果。
|
|
334
344
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
345
|
+
[v1.15.7] 用户隔离: 当 agent_knowledge_dir 已设置时,仅搜索当前
|
|
346
|
+
Agent 专属知识库,不再回退到组织知识库(避免搜索到其他用户的文件)。
|
|
347
|
+
仅在 agent_knowledge_dir 未设置时才搜索组织知识库(兼容无专属知识库的 Agent)。
|
|
338
348
|
|
|
339
349
|
Args:
|
|
340
350
|
query: 搜索查询文本(通常为 <get_knowledge> 内容或用户消息)
|
|
@@ -342,22 +352,22 @@ class ContextBuilder:
|
|
|
342
352
|
Returns:
|
|
343
353
|
<knowledge> XML 段落字符串
|
|
344
354
|
"""
|
|
345
|
-
#
|
|
355
|
+
# Agent 专属知识库已设置 → 仅搜索当前用户/Agent 的知识
|
|
346
356
|
if self.agent_knowledge_dir:
|
|
347
357
|
agent_result = self._search_knowledge_dir(self.agent_knowledge_dir, query, top_k=5)
|
|
348
358
|
if agent_result:
|
|
349
359
|
return agent_result
|
|
360
|
+
# [v1.15.7] 不再回退到组织知识库,避免搜索到其他用户的文件
|
|
361
|
+
return "<knowledge>\n(未找到相关知识)\n</knowledge>"
|
|
350
362
|
|
|
351
|
-
#
|
|
363
|
+
# 未设置 Agent 专属知识库 → 搜索组织知识库(兼容模式)
|
|
352
364
|
if self.knowledge_base_dir:
|
|
353
365
|
org_result = self._search_knowledge_dir(self.knowledge_base_dir, query, top_k=5)
|
|
354
366
|
if org_result:
|
|
355
367
|
return org_result
|
|
368
|
+
return "<knowledge>\n(未找到相关知识)\n</knowledge>"
|
|
356
369
|
|
|
357
|
-
|
|
358
|
-
if not self.knowledge_base_dir and not self.agent_knowledge_dir:
|
|
359
|
-
return "<knowledge>\n(知识库未配置)\n</knowledge>"
|
|
360
|
-
return "<knowledge>\n(未找到相关知识)\n</knowledge>"
|
|
370
|
+
return "<knowledge>\n(知识库未配置)\n</knowledge>"
|
|
361
371
|
|
|
362
372
|
def _search_knowledge_dir(self, kb_dir: str, query: str, top_k: int = 5) -> str:
|
|
363
373
|
"""在指定知识库目录中执行 RAG 搜索并格式化结果
|
|
@@ -734,8 +744,8 @@ class ContextBuilder:
|
|
|
734
744
|
other = len(text) - cn
|
|
735
745
|
return int(cn * 1.3 + other * 0.35)
|
|
736
746
|
|
|
737
|
-
#
|
|
738
|
-
window =
|
|
747
|
+
# 使用配置的 context_window(由 init_context_builder 注入)
|
|
748
|
+
window = self.context_window
|
|
739
749
|
|
|
740
750
|
budget = int(window * budget_ratio)
|
|
741
751
|
estimated = _est_tok(context_xml)
|
package/core/deps_checker.py
CHANGED
|
@@ -75,6 +75,10 @@ DEPENDENCIES: List[DepInfo] = [
|
|
|
75
75
|
# ── 语音合成 ──
|
|
76
76
|
DepInfo("edge_tts", "edge-tts", "6.1.0", "tts", "all"),
|
|
77
77
|
|
|
78
|
+
# ── 语音识别 (STT) ──
|
|
79
|
+
DepInfo("faster_whisper", "faster-whisper", "1.0.0", "stt", "all",
|
|
80
|
+
note="本地语音识别引擎 (首次使用自动下载模型)"),
|
|
81
|
+
|
|
78
82
|
# ── 浏览器自动化 (ChromeDev MCP) ──
|
|
79
83
|
# Playwright 已移除,浏览器自动化统一使用 ChromeDevTools Protocol (MCP)
|
|
80
84
|
# 需要 Node.js >= 20.19,参见 skills/chromedev_mcp.py
|
|
@@ -369,6 +373,7 @@ def ensure_skill_deps(skill_category: str) -> bool:
|
|
|
369
373
|
"gui": {"gui"},
|
|
370
374
|
"search": {"search"},
|
|
371
375
|
"tts": {"tts"},
|
|
376
|
+
"stt": {"stt"},
|
|
372
377
|
"tray": {"tray"},
|
|
373
378
|
}
|
|
374
379
|
|
package/core/llm.py
CHANGED
|
@@ -89,6 +89,7 @@ class LLMResponse:
|
|
|
89
89
|
raw_response: Any = None
|
|
90
90
|
success: bool = True
|
|
91
91
|
error: str = ""
|
|
92
|
+
reasoning: str = "" # 推理模型的思考过程 (DeepSeek-R1, o1, o3, QwQ 等)
|
|
92
93
|
|
|
93
94
|
|
|
94
95
|
# ==============================================================================
|
|
@@ -118,6 +119,7 @@ class LLMClient:
|
|
|
118
119
|
max_retries: int = 3,
|
|
119
120
|
reasoning: bool = False,
|
|
120
121
|
reasoning_effort: str = "medium",
|
|
122
|
+
context_window: int = 128000,
|
|
121
123
|
**kwargs,
|
|
122
124
|
):
|
|
123
125
|
self.provider = provider
|
|
@@ -130,6 +132,7 @@ class LLMClient:
|
|
|
130
132
|
self.max_retries = max_retries
|
|
131
133
|
self.reasoning = reasoning
|
|
132
134
|
self.reasoning_effort = reasoning_effort # low | medium | high
|
|
135
|
+
self.context_window = context_window
|
|
133
136
|
self.extra = kwargs
|
|
134
137
|
self._client = None
|
|
135
138
|
|
|
@@ -973,6 +976,7 @@ def get_llm_client() -> LLMClient:
|
|
|
973
976
|
timeout=cfg.timeout,
|
|
974
977
|
max_retries=cfg.max_retries,
|
|
975
978
|
anthropic_api_key=cfg.anthropic_api_key,
|
|
979
|
+
context_window=cfg.context_window,
|
|
976
980
|
)
|
|
977
981
|
return _global_llm_client
|
|
978
982
|
|
package/core/output_parser.py
CHANGED
|
@@ -77,6 +77,7 @@ KNOWN_TOP_LEVEL_TAGS = [
|
|
|
77
77
|
"finish_reason",
|
|
78
78
|
"next_step",
|
|
79
79
|
"response",
|
|
80
|
+
"mainsubject", # [v1.15.8] 会话标题自动命名
|
|
80
81
|
]
|
|
81
82
|
|
|
82
83
|
# Inner tags inside each <tool>.
|
|
@@ -147,6 +148,7 @@ class ParsedOutput:
|
|
|
147
148
|
finish_reason: str = ""
|
|
148
149
|
next_step: str = ""
|
|
149
150
|
response: str = ""
|
|
151
|
+
mainsubject: str = "" # [v1.15.8] 会话标题自动命名(6字以内)
|
|
150
152
|
raw_text: str = ""
|
|
151
153
|
parse_success: bool = False
|
|
152
154
|
needs_correction: bool = False
|
|
@@ -444,6 +446,10 @@ def _custom_parse(raw_text: str) -> ParsedOutput:
|
|
|
444
446
|
raw_val = _extract_tag_content(body, "next_step")
|
|
445
447
|
parsed.next_step = _safe_strip(raw_val)
|
|
446
448
|
|
|
449
|
+
# mainsubject [v1.15.8] 会话标题自动命名
|
|
450
|
+
raw_val = _extract_tag_content(body, "mainsubject")
|
|
451
|
+
parsed.mainsubject = _safe_strip(raw_val)
|
|
452
|
+
|
|
447
453
|
# ── Step 3: Parse <remember> (may contain <type> and <content>) ──
|
|
448
454
|
remember_raw = _extract_tag_content(body, "remember")
|
|
449
455
|
if remember_raw.strip():
|
package/departments/manager.py
CHANGED
|
@@ -310,6 +310,10 @@ class DepartmentManager:
|
|
|
310
310
|
**meta,
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
# 确保 agent_count 字段存在(meta 中不一定有,需要动态计算)
|
|
314
|
+
if "agent_count" not in result:
|
|
315
|
+
result["agent_count"] = len(meta.get("agents", []))
|
|
316
|
+
|
|
313
317
|
# 列出子部门
|
|
314
318
|
sub_depts = self.list_sub_depts(path)
|
|
315
319
|
result["children"] = sub_depts
|
package/main.py
CHANGED
|
@@ -178,6 +178,7 @@ class MyAgentApp:
|
|
|
178
178
|
max_retries=llm_cfg.max_retries,
|
|
179
179
|
reasoning=llm_cfg.reasoning,
|
|
180
180
|
anthropic_api_key=llm_cfg.anthropic_api_key,
|
|
181
|
+
context_window=llm_cfg.context_window,
|
|
181
182
|
)
|
|
182
183
|
self.logger.info(f"LLM: {llm_cfg.provider}/{llm_cfg.model}")
|
|
183
184
|
|
package/package.json
CHANGED