日期: 2026-04-03 ~ 2026-04-04
agent67 通过 AgentPaaS 或 run.py 执行编程任务时,出现严重的幻觉:
claude -p 无状态模式agent67 的 LLM 后端 ClaudeLam 使用 claude -p(pipe mode)调用 Claude:
每一步 ReAct:
subprocess.run(["claude", "-p", "--system-prompt", prompt], input=state)
↑ 新进程,无上下文记忆
每一步都是独立的子进程调用。Claude 没有前几步的记忆,必须从 state 字符串重建上下文。当 state 被压缩或截断时,关键信息丢失,导致 LLM:
| # | 因素 | 影响 |
|---|---|---|
| 1 | 工具输入格式不匹配 | from_config 传 dict,PersonalAssistant 传 JSON string。dict 路径跳过了 _parse_input 的容错逻辑,导致 VALIDATION_ERROR |
| 2 | MCP 工具泄漏 | claude -p 继承当前 Claude Code 会话的 MCP 配置(Vercel/Gmail),LLM 认为自己只有这些工具,拒绝使用 system prompt 中声明的工具 |
| 3 | 观察结果被截断 | PersonalAssistant 的 observations 累积后过长,context_manager.compact() 压缩丢失关键内容;from_config 的 _MAX_OBS_LENGTH=800 导致 find 输出被截断 |
| 4 | stop_condition bug | result[-200:] 检查截掉了短结果中的 [Step 标记,导致误判为最终回复 |
| 5 | 无终止验证 | LLM 调用 terminate 时可以在 summary 中编造任意内容,没有验证声称的操作是否真正执行 |
PersonalAssistant 路径 (run.py):
用户 → PersonalAssistant.chat()
→ ClaudeLam("claude -p") → LLM 输出
→ parse_and_execute() 硬编码路由
→ BUILTIN_TOOLS[name].apply(json_string)
from_config 路径 (agentpaas chat):
用户 → POST /agents/{id}/run
→ from_config("agent-config.yml") → Loop(react_step)
→ ClaudeLam("claude -p") → LLM 输出
→ _extract_tool_call() → _timeout_call(tool, dict)
PersonalAssistant 路径最初没有幻觉,是因为 json.dumps() 序列化工具输入,恰好绕过了 schema 验证的严格模式。
根本修复:引入 ConversationLam 包装层,将任意 LLMProvider 从无状态改为有状态。不再局限于 Claude Code 的 --resume,而是对所有 Provider 统一提供完整对话历史。
class ConversationLam:
"""将任意 LLMProvider 包装为有状态的对话调用"""
def __init__(self, provider: LLMProvider):
self.provider = provider
self.messages = [] # 完整对话历史
def send(self, content: str, role: str = "user") -> str:
self.messages.append({"role": role, "content": content})
response = self.provider.chat(self.messages)
self.messages.append({"role": "assistant", "content": response})
return response
不同 Provider 的底层实现各异,但对 ConversationLam 透明:
| Provider | 底层会话机制 |
|---|---|
| ClaudeCodeProvider | 首次调用获取 session_id,后续用 --resume 恢复会话 |
| OpenAICompatProvider | 每次调用传完整 messages 数组(适用于 OpenAI / DashScope / DeepSeek 等) |
react_step 因此大幅简化:
def react_step(state, step_index):
if step_index == 0:
response = conversation.send(full_input) # 完整输入
else:
response = conversation.send(latest_observation) # 只发最新 observation
# provider 内部已持有完整历史,无需 state 压缩/滑窗
效果:
无状态 (-p) |
有状态 (ConversationLam) | |
|---|---|---|
| 上下文 | 每步从 state 字符串重建 | Provider 保持完整会话记忆 |
| 文件内容 | 被压缩/截断后可能丢失 | LLM 记得之前读过的所有文件 |
| 工具调用历史 | 依赖 state 中的 [Step N] 标记 |
LLM 记得每一步的操作和结果 |
| 死循环 | 常见(反复读同一文件) | 极少(LLM 知道已经读过了) |
| 幻觉 | 频繁(丢失上下文后编造) | 零幻觉(完整记忆,已验证) |
| Provider 支持 | 仅 Claude Code | Claude Code / OpenAI / DashScope / DeepSeek 等全部支持 |
即使有了会话持久化,以下修复仍然保留以提供额外防护:
claude -p --mcp-config '{"mcpServers":{}}' --strict-mcp-config
防止 Claude 看到 Vercel/Gmail 等无关 MCP 工具。所有路径都需要。
# from_config 路径:dict → JSON string,与 PersonalAssistant 对齐
if isinstance(tool_input, dict):
tool_input_val = json.dumps(tool_input, ensure_ascii=False)
# _parse_input: LLM 常用 "path" 代替 "file_path"
_ALIASES = {"path": "file_path", "filepath": "file_path", ...}
# 从 schema 自动生成,注入 system prompt
- **ReadFile**: `{"action":"ReadFile","input":{"file_path": ..., "offset": 0, "limit": 2000}}`
- **Bash**: `{"action":"Bash","input":{"command": ..., "timeout": 120}}`
# 只保留最近 5 步完整结果,旧的压缩为摘要
if len(observations) > 5:
# 旧步骤:一行摘要
# 最近 5 步:完整内容
# --resume 后 Claude 倾向于用内置工具名 (Read/Write/Edit)
"工具名是 ReadFile(不是Read)、WriteFile(不是Write)、EditFile(不是Edit)"
python3 -m agentpaas chat lambda
# Claude Code 后端(使用 --resume 会话持久化)
python3 -m agentpaas chat lambda --provider claude-code
# OpenAI 兼容后端(传完整 messages 数组)
python3 -m agentpaas chat lambda --provider openai-compat
python3 agentexample/agent67/run.py --claude
AGENTPAAS_API_KEY=<key> python3 agentexample/agent67/launch_paas.py chat
| 文件 | 变更 |
|---|---|
lambdagent/providers/claude_code.py |
ClaudeLam 会话持久化(_session_id + --resume) |
lambdagent/providers/__init__.py |
新包,导出 ClaudeLam |
agentexample/agent67/core/assistant.py |
inject_override=False、observations 滑窗、工具名提示、max_steps=30 |
agentexample/agent67/core/claude_lam.py |
Re-export from lambdagent.providers |
agentexample/agent67/run.py |
--claude 参数 |
lambdagent/fromconfig/compiler.py |
P0-P3 修复、工具参数文档、terminate 验证 |
lambdagent/builtin_tools/file_tools.py |
ListFiles 默认 pattern、参数别名 |