我最近被问得最多的一个问题是:你的 agent 到底用的哪个模型?说真的,模型早就不是瓶颈了。真正让一个 agent 在一半的时候"卡死""乱答""罢工"的,往往是那些看起来特别无聊的工程问题——数据拿不到、权限不匹配、上下文塞爆。这也是为什么我看到 "Show HN: Give every agent the right data, access and context to get work done" 这个标题时,突然很有感触。它把 agent 开发最核心的三个支柱总结得非常准确:data(数据供给)、access(访问控制)、context(上下文管理)。Agent 能不能靠谱地把活干完,拼的就是这三件事。这篇文章我想抛开花哨的概念,直接从实战出发聊聊:为什么这三样东西是 agent 的命根子,以及我在具体项目里是怎么把它们落地的。
1. "Give every agent the right data, access and context"到底在解决什么问题
1.1 先说我为什么关注这个命题
我最早做 agent 的时候,犯过一个特别蠢的错误。当时团队做了一个文档问答 agent,内部测试效果不错,于是想让销售部门用起来。结果一上线就发现,agent 有时候回答得特别好,有时候却像完全失忆一样,连最简单的"上季度合同总额"都能编一个数。排查了半天,发现不是模型问题,而是它查数据时连的是测试库,权限配置又错了,导致部分字段返回 null,模型以为是"没有数据",于是开始一本正经地瞎编。
从那以后我意识到一个道理:agent 的专业能力,很大程度不取决于模型智商,而取决于你喂给它的数据和它触达资源的方式。再强的模型,拿不到数据、碰不了内部系统、记不住关键上下文,就是一辆没有油的法拉利。
1.2 三个词分别对应 agent 工程里的哪三座山
把这个标题拆开看,会发现它其实是一个很完整的工程纲领。
- Data:你要让 agent 能"拿到正确的数据"。这个正确有两层意思,一层是内容维度,它需要的是高质量、高相关性的数据,而不是一堆乱七八糟的日志;另一层是形态维度,是直接给文本、走 RAG 检索,还是通过调用内部 API 拿结构化结果,这决定了数据管道怎么设计。
- Access:你要让 agent 能"碰到该碰的系统"。数据库、内部接口、第三方服务、文件系统,每一个都是需要授权才进得去的门。这里最难的不是"给权限",而是"只给刚好的权限,且随时能收回"。
- Context:你要让 agent 能"记住该记住的上下文"。对话历史、工具返回结果、用户指令、任务中间状态,这些都要在有限的 context window 里做好排布。很多 agent 做不好长任务,原因不是不聪明,而是做到一半"忘事"了。
这三件事如果拆开做,每一件都不算特别难;难的是把它们放在同一个系统里统一管理。这也正是我觉得这个标题值钱的地方——它提醒所有做 agent 的人,别只顾着调 prompt,要像给员工配办公资源一样,给每个 agent 配置一套"数据 + 权限 + 上下文"的完整工作环境。
2. Data:agent的"饭"要从按需供应的管道里来,而不是一次性塞进碗里
2.1 把整个知识库塞进prompt,是agent项目最贵的偷懒
很多 agent 项目的第一版都特别粗暴:把公司文档全部导出成 Markdown,然后往 prompt 里一拼,告诉模型"以下是你的知识库,请回答问题"。本地 demo 的时候没什么感觉,因为文档量小;一旦放到生产环境,文档几百份、上千万字,问题马上就来了。
首先是成本问题。现在主流模型的上下文窗口虽然越来越大,动辄 100 万 token,但这不意味着你可以随便浪费,请求价格是跟着 token 数走的,一次塞 50 万 token 进去,调用一次的成本就能吓死人。其次是效果问题,模型在处理超长上下文时,对中间部分的注意力会明显衰减,真正关键的信息如果埋在长文档里,它可能压根"看"不见。
我自己的经验是:数据供给一定要做成"按需取数",而不是"一次给全"。也就是你给 agent 的不是数据本身,而是一个能够检索数据、调用数据接口的能力,让它只把当前任务真正需要的那一小块数据拉进上下文。
2.2 数据接入层的核心设计:注册、检索、切片、增量
我在项目里落地数据层的时候,基本遵循四个步骤,缺一个都会在后面出幺蛾子。
第一步是注册。所有 agent 能访问的数据源,都需要在统一的数据注册表里登记。这个注册表记录了数据源的类型(数据库、API、文档库、Excel)、描述信息、更新频率、权限标签和负责人。注册的好处是,agent 的工具选择不再靠硬编码,而是可以根据任务的语义描述,动态找到合适的数据源。
第二步是检索。对非结构化文档,走向量检索加关键词召回的组合方案;对结构化数据,则包装成标准的查询 API。这个阶段最容易踩的坑是"召回了一堆东西,但和当前问题毫不相关",我后面会专门讲怎么通过权限标签和摘要过滤把无关内容挡在外面。
第三步是切片。文档不是整块丢给模型的,而是按语义切成合适大小的 chunk,chunk 太小了检索会碎片化,太大了检索精度又下降。不同业务文档的最佳切片粒度差异很大,代码库和制度文档的切片策略就不能一样。
第四步是增量。知识库不是静止的,今天加的合同、明天删的政策,都会影响 agent 回答的准确性。所以我通常会做一个定时同步和变更检测的管道,保证 agent 拿到的数据是"今天"的,而不是"三个月前"的。
2.3 真实翻车现场:data parcel size与"啥都能查"的伪需求
数据层如果不做控制,最直接的结果就是各种运行时崩溃。我之前调试一个 Android 端的 agent 助手时,遇到过这样一个报错:android.os.TransactionTooLargeException: data parcel size 1050332 bytes。当时很奇怪,明明只是查了一组列表数据,为什么会把 Binder 事务缓冲区撑爆?排查之后发现,是 agent 调用的查询接口没有做字段裁剪和分页,一次把一个大表 90% 的字段全部返回了,接在 agent 后面再做本地渲染时,数据量直接爆掉。
这个案例特别典型。它说明一个问题:如果数据管道在设计时就抱着"反正模型能处理多长的上下文,我就把所有数据都给它"的心态,那么从接口到传输层再到上下文窗口,每一个环节都会被撑爆。"啥都能查"听起来很美,实际上是对系统极不负责的伪需求。
更常见的还有ERROR 1045 (28000): Access denied for user 'root'@'localhost'这类数据库权限报错。很多团队图省事,直接用 root 账号跑 agent 的数据库工具,结果要么是密码写死在环境变量里,被同事不小心提交到 Git 仓库,要么就是权限过大,让 agent 拥有删库的能力。千万不要把生产数据库的原生访问方式直接暴露给 agent,更合理的做法是在数据库前面包一层语义化的数据服务 API,字段名和操作类型都由后端统一控制,agent 只能按照服务定义好的方式去取数。
3. Access:权限不是"给个key",而是"给一个随时会吊销的身份"
3.1 常见误区:用一个人账号的token跑所有agent
权限这块是我见过翻车最多的地方。最典型的一个场景是:把个人的访问令牌(access token)直接配到 agent 的配置里,让所有 agent 共享同一个人身份。
看起来挺方便的,但隐患很大。你肯定遇到过手机或某个网页工具突然弹出一句your access token could not be refreshed. please log out and sign in again.,在个人工具里这只是一次烦人的重新登录,但如果是多个 agent 共享同一个 token,后果就是一个人 token 失效,所有 agent 集体罢工。更危险的是,个人账号通常拥有比 agent 实际需求大得多的权限,一旦 agent 被提示注入攻击,或者生成了错误的工具调用,它可能就拿着这个超管身份把内部系统搅得天翻地覆。
我的原则是:agent 必须要有自己的独立身份,和任何具体员工的身份完全解耦。它在系统里就像一个"数字员工",有自己的用户名、角色、权限范围,管理员随时可以把它停用或者吊销凭证,而不影响任何真人账号。
3.2 最小权限、短期凭证和按工作流授权
给 agent 配权限,我一般遵循三个原则。
一是最小权限。只在 agent 真正需要执行的任务范围内授权。一个只负责查库存的 agent,就不应该拥有修改库存的权限,更不应该有删除日志的权限。这里的粒度要细化到 API 方法级别,而不只是数据表级别。
二是短期凭证。agent 拿到的访问凭证应该有明确的生命周期,比如有效期 15 分钟或者 1 小时,过期之后就要用它的独立身份去刷新。这样即使凭证泄露,影响面也是有限的。我在设计里会把"身份申请"和"凭证签发"做成独立的服务,agent 每次启动任务前先去申请一次短期凭证,而不是拿着一个永久 token 到处跑。
三是按工作流授权。不要给 agent 一个全局大权限,而是把权限绑定到工作流节点上。比如一个"合同审核"工作流,第一步需要读取合同文档,第二步需要调企业微信接口通知相关人,第三步需要写入审核结论。那么这个 agent 的权限就应该被拆成三次小授权,分别对应这三个步骤,而不是一开始就给它整个文档库和通讯录的访问权。
3.3 权限边界在multi-agent场景下最容易失守
单 agent 的权限还好管理,一到了 multi-agent(多智能体协作)场景,权限边界就特别容易出问题。
我之前参与过一个内部系统,里面有三个 agent 协作处理工单:一个负责收集问题信息,一个负责查用户历史记录,一个负责生成处理建议。一开始的设计是每个 agent 直接对接底层系统,于是每个 agent 都被授予了比较大的权限。结果有一次安全扫描发现,第一个 agent 的输入里被人注入了恶意指令,导致它越权调用了"更新工单状态"的接口,虽然很快被发现,但这件事给我敲了警钟。
多 agent 协作时,权限模型里一定要有"委托"和"身份传播"的概念。当 agent A 需要调用 agent B 的能力时,A 不应该直接把 B 的权限拿过来用,而是应该带着"我受 A 的委托来执行这个任务"这样的上下文,由统一代理层决定这个调用是否被允许。这就像公司里跨部门协作,员工不能因为认识财务部的人就直接动人家的账,必须走正式的委托流程。
4. Context:上下文是硬通货,context overflow是第一个要治的病
4.1 一个agent失败,往往不是不会做,而是"忘了"该做什么
上下文问题在长任务场景中会变得特别突出。很多 agent 跑着跑着,突然开始回答一些和最初目标完全无关的内容,或者把前面几轮已经确认过的事实又推翻重来。这种情况,十有八九是上下文管理出了问题。
现在的模型依靠 context window 来"记忆",但这个窗口是有限的。虽然很多模型宣称支持超长上下文,比如 1048576 token,但实际用起来你会发现,上下文越长,模型对早期信息的引用能力就越差,而且处理速度会明显变慢,成本也会直线上升。我一直把上下文当成一种"硬通货"来管理——它是有预算的,不是拿来就用的。
4.2 上下文预算:40万token也会被乱用耗尽
我见过最离谱的一次事故,是一个 agent 在执行数据分析任务时,把整个数据表的原始内容全放进了上下文,然后又循环调用工具,把每次工具返回的完整冗余结果都留在上下文里。结果任务跑到一半,模型直接报了类似context overflow: this conversation is too large for the model. try /compact的错。在代码生成场景里,Claude Code 这类工具也经常会因为历史消息太多,出现codex ran out of room in the model's context window. start a new thread or c...的提示,原理都是一样的。
用大白话说:上下文就是 agent 的"工作台",工作台只有那么大,你不能把仓库里所有货都堆上去。正确的做法是对放置在工作台上的每一样东西都做预算。
我自己会给上下文划分几个明确的部分:
- 系统指令:固定描述 agent 的角色、任务、行为准则,这部分尽量精简,只放不可妥协的规则。
- 任务上下文:用户当前提交的任务和关键约束。
- 数据片段:通过检索或者工具调用拿回的和当前任务直接相关的数据。
- 历史摘要:此前若干轮对话的高度压缩版本,而不是原始轮次。
- 工具说明:当前任务马上要用到的工具的描述和参数 schema。
4.3 压缩、检索与状态恢复:实测可用的三件套
针对 context overflow,我实测最有效的组合拳有三招。
第一招是摘要压缩。当对话轮次超过一定阈值,不直接丢弃早期消息,而是让模型把早期消息浓缩成一段摘要,存到上下文的最前面。这样既保留了关键信息,又大幅减少了 token 占用。
第二招是相关性检索。把每次工具调用产生的大段返回结果,先做一轮相关性过滤和字段截断,只保留任务真正需要的字段和片段。很多工具返回的 JSON 有几百个字段,实际用到的可能就四五个。我会用一个清洗层把用不到的字段删掉,再放进上下文。
第三招是状态恢复。一旦上下文真的溢出,或者任务执行被中断,不要直接让 agent 从头再来,而是设计一个"checkpoint"机制。每完成一个阶段,让 agent 输出当前进度、已经确认的结论、下一步计划,然后把这份状态压缩保存。重新启动时,先恢复状态再继续干活,这样即使上下文被清空,任务上下文也不会丢失。这比单纯依赖/compact命令要稳健得多,因为它强调"恢复",而不只是"压缩"。
5. 整合落地:把data、access、context当成agent平台的三个一等公民
5.1 资源描述与策略分离:一个简单的配置模型
看完上面的内容,你应该能感受到:data、access、context 这三件事不是孤立存在的,它们需要在一个统一的框架下面被治理。我自己在项目里采用的思路是"资源描述与策略分离"。
简单来说,就是把"一个 agent 能访问什么数据、以什么身份访问、把这些数据以什么方式放进上下文"这三件事,拆成三层配置,互不干扰。
我把这个配置模型称为"Agent 资源卡片"(Agent Resource Profile)。下面是一个简化的 YAML 案例:
agent: name: order_analysis_agent role: order_analyst identity: svc-order-analysis data: sources: - source: orders_db type: mysql permission_label: finance_read - source: product_catalog type: rag_index permission_label: public_read retrieval: chunk_size: 512 top_k: 5 access: scopes: - "db:orders:select" - "api:product:read" credential: lifespan: 15m refresh_mode: auto context: budget: system: 1000 task: 4000 data: 6000 history: 2000 compression: enabled: true threshold: 8000 checkpoint: enabled: true interval: 3这份配置的含义是:
- agent 有自己独立的服务身份
svc-order-analysis,不借用任何个人账号; - 它只能访问订单库的 select 权限和商品目录的读取权限,无法写数据;
- 它的凭证有效期只有 15 分钟,自动刷新;
- 它的上下文预算被明确划分给系统指令、任务、数据、历史四个区域,避免某一项无限膨胀。
5.2 关键代码:一个provisioning模块的骨架
配置有了,还需要一个真正能落地的 provisioning(资源装配)模块。它的职责是:在 agent 启动任务之前,把配置翻译成实际可用的东西——具体的检索接口、具体的短期凭证、具体的上下文初始内容。
我用 Python 写了一个最小实现,思路可以直接复用:
class AgentProvisioner: def __init__(self, profile_path): self.profile = self._load_profile(profile_path) def provision(self, task: str) -> AgentWorkspace: # 1. 根据权限标签,拿到可访问的数据源清单 data_sources = self._resolve_data_sources(self.profile["data"]) # 2. 申请短期凭证,而不是使用长期 token credential = self._request_credential( identity=self.profile["agent"]["identity"], scopes=self.profile["access"]["scopes"], lifespan=self.profile["access"]["credential"]["lifespan"] ) # 3. 根据任务描述,预先检索一批高相关数据片段 initial_context = self._retrieve_initial_context( task=task, sources=data_sources, top_k=self.profile["data"]["retrieval"]["top_k"] ) # 4. 构建上下文预算管理对象 context_budget = ContextBudget(**self.profile["context"]["budget"]) return AgentWorkspace( data_sources=data_sources, credential=credential, initial_context=initial_context, context_budget=context_budget )这段代码的逻辑很直白,但它代表了一个很重要的设计转变:agent 不再自己去"找"数据和凭证,而是在任务开始时,由一个统一的服务把资源和权限一次性装配好。这样做的好处是,所有 agent 的资源获取路径是收敛的,出了问题,只要查 provisioning 这一个环节就行。
5.3 这套方案天然解决"harness和agent"的职责划分
说到这儿,正好可以回应一个我经常被问到的问题:agent harness 和 agent 本体到底有什么区别?
我的理解是:harness(管理框架)负责的是"干活的环境",包括资源装配、工具调用、上下文预算、生命周期、审计日志;agent 本体负责的是"干活的脑子",就是根据输入做推理决策。这两者必须分离,否则 agent 本体既要思考,又要管权限和内存,代码很快就会乱成一团。
前面说的 AgentProvisioner,本质上就是 harness 的一部分。data、access、context 这三层资源装配,应该全部由 harness 在后台完成,agent 的核心逻辑只要面对一个干净的工作空间——我拿到了一堆我有权访问的数据片段、一把只对当前任务有效的钥匙、一个清晰的上下文预算,然后开始思考怎么干活。
把这三件事放进 harness 里还有一个额外的好处:可观测性。每一次数据访问、每一次凭证签发、每一次上下文压缩,都有日志可查。出了问题,不是对着黑盒瞎猜,而是直接看资源装配链路在哪一步断了。
最后说点实际的
如果你现在正准备做一个 agent 项目,我的建议是别急着选模型、调 prompt,先把 data、access、context 这三件事画出来。哪怕一开始只是用最简单的配置文件把数据源列清楚、给 agent 分配一个独立账号、给上下文设一个总预算,也比什么都不做要强。我个人的体会是,这套思路带来的最大变化不是"功能变多了",而是"故障变少了"——agent 莫名其妙失忆的情况少了,权限事故少了,接口被一把梭调爆的情况也少了。它不性感,但它能让你把精力重新放回真正有价值的业务逻辑上。