Lazy loaded image
Agent丨你做过的 AI 项目中最困难的技术挑战是什么,你是如何解决的?
Words 6219Read Time 16 min
2026-5-2
type
Post
status
Published
date
May 2, 2026
slug
agent27
summary
tags
Agent
category
icon
password

一、最困难的挑战:是让Agent“稳定、可控、可验证”地调工具

我做过最困难的 AI 项目,是一个面向研发团队的 AI Coding + RAG Agent 平台
它是一个能在真实代码仓库中完成以下任务的 Agent:
理解需求 → 检索代码与文档 → 制定修改计划 → 调用工具读写文件 → 执行测试 → 修复失败 → 生成变更说明 → 提交 PR 草稿。
一开始我以为难点是:
如何让 LLM 调用工具?
如何让 RAG 检索更准?
如何让模型写出更好的代码?
后来发现真正困难的是:
当 Agent 进入真实工程环境后,它会面对长上下文、多工具、多轮失败重试、脏数据、权限风险、工具输出噪声、子 Agent 失控、历史记忆污染等问题。难点不再是“会不会调用工具”,而是“调用工具之后系统是否仍然可控”。
Claude Code 的 Agent SDK 官方文档里明确把读文件、运行命令、编辑代码、搜索代码库、Hooks、Subagents、MCP、Permissions、Sessions 等能力作为 Agent SDK 的核心组成部分,这说明成熟 Coding Agent 的工程重心已经从“单次问答”转向了完整的工具执行与上下文管理系统。(Claude API Docs)
OpenCode 也采用了类似思路:它把工具调用、权限控制、内置工具、Agent 类型、子 Agent 和会话机制作为核心能力,其中工具包括 bash、edit、write、read、grep、glob、lsp、apply_patch、webfetch、websearch 等。(OpenCode) OpenClaw 则进一步把 Agent 放到 Gateway、WebSocket、多通道、节点、沙箱、技能、插件和长期会话体系里,说明生产级 Agent 的难点本质上是一个 分布式控制系统问题,而不是简单 prompt 工程问题。(OpenClaw)

二、项目背景:我们要做的不是 Copilot,而是“半自动研发执行器”

项目目标是构建一个内部研发 Agent,类似 Claude Code / OpenCode / Devin 类产品的核心能力,但针对公司自己的代码库、研发规范、测试环境和知识库做定制。
核心场景包括:
  1. 根据 Jira / GitHub Issue 自动理解需求。
  1. 检索相关代码、历史 PR、架构文档、接口文档。
  1. 生成变更计划。
  1. 自动修改多文件代码。
  1. 自动运行单测、类型检查、lint、构建命令。
  1. 根据失败日志二次修复。
  1. 输出 PR 描述、风险点、回滚方案。
最初版本非常简单:
这个版本在 Demo 环境里效果很好,但一进真实项目就开始暴露问题。

三、第一版为什么失败?

1. RAG 检索到的内容“语义相关但工程无效”

比如用户说:
给订单退款流程增加风控校验。
向量检索经常能召回这些内容:
refund、risk、order、payment、settlement、audit
语义上都相关,但工程上不一定可用。常见问题有:
检索到旧版本接口。
检索到废弃模块。
检索到测试 mock,不是真实调用链。
检索到业务文档,但缺少代码入口。
检索到相似文件,但不是当前服务的实现。
所以 Agent 会出现一种非常危险的行为:
它看起来引用了上下文,实际上引用的是“错误但看似合理”的上下文。
这比完全没有 RAG 更危险。

2. 工具输出把上下文撑爆

真实代码库里,一个 grepripgrepnpm testpytestmvn test 的输出都可能非常大。
第一版 Agent 会直接把工具输出塞回 LLM:
问题是:
失败日志很长,真正有用的只有前几个 error stack。
LLM 会被重复 warning、无关日志、依赖安装信息干扰。
上下文窗口被快速消耗。
后续重要代码片段被挤出去。
OpenCode 早期文档里就包含 auto compact 能力,会在对话接近上下文窗口限制时触发总结,以避免长会话失控;当前 OpenCode 文档也把 hidden compaction / summary agent 放在内置 Agent 体系中。(GitHub) (OpenCode) Claude Code 的 Skills 文档也强调,Skill 主体只有在使用时才加载,从而降低长参考材料的上下文成本。(Claude API Docs)

3. 工具权限不是“有没有权限”这么简单

第一版只有简单开关:
这在生产里完全不够。
因为 bash 不是一个工具,而是一个 能力入口
同一个 bash 工具,既可以安全执行 npm test,也可以执行危险命令。
OpenCode 的权限模型将动作分成 allowaskdeny,并支持对 bash、edit 等工具做更细粒度的通配符规则;例如 git *npm * 可以 allow,而 rm * 可以 deny。(OpenCode) Claude Code 的 hooks 文档也强调 hooks 可以在 Claude Code 生命周期的关键点运行确定性逻辑,用来格式化文件、阻止命令、注入上下文、发送通知等,而不是完全依赖 LLM 自觉。(Claude API Docs)

4. 子 Agent 让问题变成“递归系统问题”

我们后来引入了多 Agent:
Planner:负责计划。
Explorer:负责代码检索。
Coder:负责修改。
Reviewer:负责审查。
Verifier:负责运行测试。
这看起来很优雅,但新问题出现了:
主 Agent 有权限控制,子 Agent 是否继承?
子 Agent 能否继续创建子 Agent?
子 Agent 的工具输出如何汇总回主 Agent?
子 Agent 的错误上下文会不会污染主会话?
子 Agent 是否可能绕过主 Agent 的安全策略?
这个问题不是理论上的。OpenCode 的历史 issue 中就出现过类似风险:有 issue 报告 tool.execute.before 插件 hook 能阻止 primary agent 的工具调用,却没有拦截通过 task tool 产生的 subagent 工具调用,导致安全策略被绕过;另一个 issue 报告 task tool 忽略 per-target deny,允许 agent 自我派发,从而造成递归 subagent loop。(GitHub) (GitHub)
所以我们最终意识到:
多 Agent 的核心不是“多几个角色 prompt”,而是 权限继承、上下文隔离、任务边界、递归限制、审计链路
Claude Code 和 OpenCode 都有内置 subagent 概念。Claude Code 的 Explore / Plan / General-purpose subagents 会根据任务场景被委派,并且 Explore / Plan 这类 agent 会限制为只读工具;OpenCode 也区分 primary agents 和 subagents,内置 Build、Plan、General、Explore、Scout 等不同权限和职责的 Agent。(Claude API Docs) (OpenCode)

四、最终解决方案:把 Agent 拆成四个平面

我们最后没有继续堆 prompt,而是把系统拆成四个平面:
Context Plane:上下文平面,决定给模型什么信息。
Control Plane:控制平面,决定模型能做什么。
Execution Plane:执行平面,负责真实工具调用。
Verification Plane:验证平面,判断结果是否可信。
整体架构如下:
这四个平面的设计原则是:
LLM 只负责推理和决策,不直接拥有执行权。
工具调用必须经过策略层。
工具结果必须经过压缩和结构化。
代码修改必须经过验证闭环。
子 Agent 必须有清晰的能力边界。
OpenClaw 的文档里也有类似分层思想:Gateway 作为长生命周期控制平面,维护消息通道、WebSocket API、节点、事件和会话;工具执行可以进入 sandbox,而 Gateway 本身留在 host 上。(OpenClaw) (OpenClaw)

五、核心设计一:Context Pack,而不是直接把 RAG 结果塞给模型

我们把 RAG 结果重构成 ContextPack
第一版:
改造后:
关键变化是增加了三类元信息:
freshness:这段信息是否过期。
trust:这段信息是否可信。
reason:为什么它被放进上下文。
真正喂给 LLM 的不是原始 chunk,而是经过筛选、排序、去重、压缩后的上下文包。
这里最重要的是 negativeEvidence
很多 RAG 系统只告诉模型:
这些内容相关。
但生产级 Agent 还要告诉模型:
哪些内容虽然相关,但不能直接信。
哪些接口已经废弃。
哪些文件只是测试 mock。
哪些历史 PR 已经被 revert。
这能显著降低“基于错误上下文的自信修改”。

六、核心设计二:工具调用必须经过 Policy Gate

成熟 Agent 的工具系统通常不是简单函数调用,而是:
LLM 生成 tool call。
Runtime 解析 tool call。
Policy 判断是否可执行。
Hook 记录和拦截。
Executor 执行。
Observation 结构化返回。
OpenCode 文档里明确说明工具允许 LLM 在代码库中执行动作,并且可以通过 permission 控制 allow、deny 或 require approval。(OpenCode) OpenClaw 文档也强调,工具可见性会受到 active profile、allow/deny policy、provider restriction、sandbox state、channel permissions、plugin availability 等因素影响,而且 tool policy 会在 model call 前执行;如果策略移除了某个工具,模型这一轮就看不到该工具 schema。(OpenClaw)
我们的工具调用中心如下:
具体策略:
执行入口:
这里的关键点是:
权限判断不写在 prompt 里,而写在 runtime 里。
Prompt 只能建议,Policy 才能约束。
所有 Agent、Subagent、MCP Tool、Custom Tool 都必须走同一个入口。
这正是我们从 OpenCode 历史 issue 中吸取的教训:如果 primary agent 和 subagent 不走同一条工具执行链路,策略就可能被绕过。(GitHub)

七、核心设计三:Observation Normalizer,工具结果不能原样回填

工具结果必须转换成结构化观察结果。
错误写法:
正确写法:
测试输出压缩:
这样 LLM 看到的是:
哪些测试失败。
哪些文件相关。
栈顶在哪里。
可能的下一步是什么。
原始日志是否被截断。
而不是几万行噪声。

八、核心设计四:多 Agent 不是角色扮演,而是能力隔离

我们最终采用的 Agent 结构如下:
每个 Agent 有不同权限:
派发逻辑:
这里的几个硬约束非常重要:
子 Agent 不能无限递归。
子 Agent 默认不再创建子 Agent。
子 Agent 不能自我派发。
子 Agent 权限不能超过父任务允许的能力。
子 Agent 的输出必须摘要回主 Agent,而不是全量合并上下文。
Claude Code 文档提到 Plan subagent 用于在 plan mode 中研究代码库,并且 subagents 不能继续 spawn subagents,以避免无限嵌套;OpenCode 文档也明确区分 primary agents 和 subagents,并支持不同 agent 配置 prompt、model、permission 等。(Claude API Docs) (OpenCode)

九、核心设计五:Verification Loop,让 Agent 对结果负责

第一版 Agent 最大的问题是:
它会说“我已经修复了”,但实际上测试没过。
最终我们把“完成”定义为状态机,而不是一句自然语言。
核心循环:
这里有一个关键原则:
Agent 不是生成代码后就结束,而是要通过测试、diff、审查和风险评估之后才结束。
Claude Code 的 Skills 文档里也提到 /run/verify/run-skill-generator 这一组能力,用来启动应用并确认变更在运行环境中有效,而不是只依赖测试或类型检查。(Claude API Docs) 这和我们的经验一致:生产级 Agent 必须从“生成答案”转向“验证结果”。

十、核心设计六:把 Prompt 固化成 Skill,而不是每次复制粘贴

后期我们发现,很多团队规范不应该每次塞进系统 prompt。
比如:
如何启动项目。
如何跑测试。
如何生成数据库 migration。
如何处理 monorepo。
哪些目录不能改。
PR 描述格式。
回滚方案模板。
这类内容适合沉淀成 Skill。
示例:
Then run:

4. Final response

Include:
  • Files changed
  • Why each change was needed
  • Tests run
  • Risks
  • Rollback plan
审计写入:
高风险判断:
这套机制的目的不是阻止 Agent 工作,而是让它在可控范围内工作。

十二、一次真实任务的执行链路

以“给退款流程增加风控校验”为例,最终链路如下:
这里的核心不是模型一次写对,而是系统允许模型安全地错、可控地修、可验证地收敛。

十三、最终效果:从“能 Demo”到“能进生产”

上线前后的差别主要体现在几个维度:

1. 成功率提升

第一版 Agent 经常出现:
找错文件。
改了测试没改业务。
改了业务没跑测试。
测试失败后胡乱修。
上下文爆掉后忘记任务目标。
改造后,任务成功率提升主要来自:
Context Pack 降低错误上下文。
Verification Loop 降低假完成。
Observation Normalizer 降低日志噪声。
Multi-Agent 隔离降低主上下文污染。
Policy Gate 降低危险操作。

2. 可解释性提升

以前 Agent 的行为像黑盒:
为什么改这个文件?
为什么运行这个命令?
为什么认为测试通过?
后来每一步都有审计:
哪个 Agent 做的?
基于哪些 Evidence?
调用了哪个 Tool?
Policy 为什么允许?
输出被如何压缩?
验证结果是什么?

3. 安全性提升

最重要的改进是:
权限不再由 prompt 约束,而由 runtime 约束。
子 Agent 不再默认继承无限能力。
MCP / Custom Tool 不再绕过统一 Policy。
高风险命令需要确认或直接拒绝。
所有工具调用可审计、可回放、可追责。

十四、我最大的技术收获

这个项目让我对 Agent 系统有了一个很深的认识:
LLM Agent 的核心不是“大模型会思考”,而是“工程系统能否把思考、行动、观察、验证、安全边界组织成稳定闭环”。
如果只做 Demo,ReAct 循环就够了:
但生产系统必须是:
真正困难的地方在这些细节:
RAG 不是召回越多越好,而是要给模型“可信上下文”。
工具不是暴露越多越好,而是要有“最小权限”。
多 Agent 不是角色越多越好,而是要有“能力边界”。
Memory 不是记得越多越好,而是要防止“信息污染”。
测试不是最后跑一下,而是 Agent 收敛过程的一部分。
Prompt 不是系统的安全边界,Runtime 才是。

十五、总结

如果让我用一句话回答“最困难的技术挑战是什么”,我的答案是:
最困难的不是让 AI 写代码,也不是让它调用工具,而是构建一个在真实工程环境中能长期运行的 Agent 控制系统:它要能选择正确上下文、调用正确工具、限制危险行为、压缩噪声结果、管理多 Agent 协作,并通过测试和审查证明自己真的完成了任务。
解决方案不是单点优化,而是一整套工程化架构:
用 Context Pack 代替原始 RAG 拼接。
用 Policy Gate 代替 prompt 里的安全提醒。
用 Observation Normalizer 代替原始工具输出。
用 Readonly / Coder / Verifier / Reviewer 多 Agent 隔离能力。
用 Verification Loop 代替“模型说完成”。
用 Skill 固化项目工作流。
用 Sandbox、Audit、Human Approval 兜住执行风险。
这也是我认为 Claude Code、OpenCode、OpenClaw 这类成熟 Agent 给我们最大的启发:生产级 Agent 不是一个模型调用封装,而是一套围绕模型构建的上下文、工具、权限、会话、验证和安全系统。
回到首页