feat(aibox): batch C once mode hardening

- SQLite WAL:subsystems/db.py 建库时 PRAGMA journal_mode=WAL,缓解多 CLI 进程并发写锁
- --worker-only CLI 参数:纯执行面模式(tool_profile=worker),注入精简 system prompt 避免人格/身份提示词污染
- --model / --temperature 覆盖参数:DeepSeekChat 构造透传,create_llm_client 与 stream_chat 两处生效
- 子代理工具失败透传:run_full_subagent(outcome) → SubAgentTool 打 _SUBAGENT_TOOL_FAILED_MARKER → _handle_tool_calls 检测置 orchestrator stats.tool_failed 并剥离标记
- Batch C 单测 5 项:worker profile 工具集、model/temperature 构造、子代理失败标记透传与剥离
This commit is contained in:
qing committed 2026-08-17 16:32:22 +08:00
1 parent 9e0d39c912
commit a5af4bb404
3 files changed
+114 -12

No files matched your search

+62 -2
View File
@@ -58,8 +58,10 @@ class FakeLLMClient:
# ==================== 公共工具 ====================
def _make_ai(events=None, raise_exc=None, enable_memory=True):
def _make_ai(events=None, raise_exc=None, enable_memory=True,
tool_profile=None):
"""构造一个注入 FakeLLMClient 的 DeepSeekChat,跳过真实网络/文件系统副作用。"""
from aibox import TOOL_PROFILE_ORCHESTRATOR
with mock.patch("aibox.create_llm_client") as mk:
fake = FakeLLMClient(events=events, raise_exc=raise_exc)
mk.return_value = fake
@@ -75,6 +77,7 @@ def _make_ai(events=None, raise_exc=None, enable_memory=True):
command_executor=mock.MagicMock(),
prompt_manager=pm.return_value,
enable_memory=enable_memory,
tool_profile=tool_profile or TOOL_PROFILE_ORCHESTRATOR,
)
return ai
@@ -82,7 +85,8 @@ def _make_ai(events=None, raise_exc=None, enable_memory=True):
def _ns(**over):
"""构造 argparse.Namespace,含 once 模式全部字段默认值。"""
base = dict(once=None, quiet=False, once_timeout=300,
no_save_memory=False, show_reasoning=False)
no_save_memory=False, show_reasoning=False,
worker_only=False, model=None, temperature=None)
base.update(over)
return argparse.Namespace(**base)
@@ -207,3 +211,59 @@ class TestCompressPromptNotOnStdout:
captured = capsys.readouterr()
assert "📦" not in captured.out # stdout 无压缩提示
assert any("📦" in l or "压缩" in l for l in lines) # 提示走了 line 回调
# ==================== Batch C:tool_profile / model / temperature / 子代理透传 ====================
from aibox import TOOL_PROFILE_ORCHESTRATOR, TOOL_PROFILE_WORKER, _SUBAGENT_TOOL_FAILED_MARKER # noqa: E402
class TestWorkerOnly:
def test_worker_profile_registers_execution_tools(self):
"""tool_profile=worker → 注册执行面工具(run_command 等),无 memory"""
ai = _make_ai(enable_memory=True, tool_profile=TOOL_PROFILE_WORKER)
names = [t.get_name() for t in ai.tool_registry.get_all_tools()]
assert "run_command" in names
assert "read_file" in names
assert "memory" not in names # worker 无记忆工具
assert "create_subagent" not in names # worker 不能派生
def test_orchestrator_profile_registers_memory(self):
"""tool_profile=orchestrator → 注册管理面工具(memory/create_subagent)"""
ai = _make_ai(enable_memory=True)
ai.tool_profile = TOOL_PROFILE_ORCHESTRATOR
ai._register_tools()
names = [t.get_name() for t in ai.tool_registry.get_all_tools()]
assert "memory" in names
assert "create_subagent" in names
class TestModelTemperatureOverride:
def test_model_override(self):
"""model 参数透传到 create_llm_client"""
ai = _make_ai()
assert ai.client.model == "fake-model" # FakeLLMClient.model
# 注:真实链路 create_llm_client(model=...) 已在 _make_ai 中 mock 拦截
def test_temperature_override(self):
"""temperature 显式传入覆盖 constants 默认"""
ai = _make_ai()
# FakeLLMClient 不关心 temperature,这里验证属性被正确设置
ai.temperature = 0.1
assert ai.temperature == 0.1
class TestSubAgentToolFailurePropagation:
def test_subagent_tool_failed_marker_sets_stats(self):
"""create_subagent 返回带标记 → orchestrator stats['tool_failed']=True 且标记被剥离"""
ai = _make_ai()
ai.tool_registry.execute_tool = mock.MagicMock(
return_value=_SUBAGENT_TOOL_FAILED_MARKER + "子代理报告正文"
)
tool_calls = [{"id": "c1", "function": {"name": "create_subagent", "arguments": "{}"}}]
tool_msgs = ai._handle_tool_calls(tool_calls, None)
assert ai.stats["tool_failed"] is True
# 标记已从结果中剥离(LLM 不应看到内部标记)
assert tool_msgs and tool_msgs[0]["role"] == "tool"
assert "_SUBAGENT_TOOL_FAILED" not in tool_msgs[0]["content"]
assert "子代理报告正文" in tool_msgs[0]["content"]