OpenClaw 动态上下文配置怎么玩?从踩坑到跑通的完整教程(2026)
上周 OpenClaw 生态突然火了,掘金热榜好几篇都在聊 Skills 体系,我也跟风研究了一下。说实话,OpenClaw 的 Skill 编排能力确实强,但它的动态上下文配置才是真正让我觉得「这东西能用到生产」的核心功能——你可以根据不同的对话阶段、用户意图,动态切换注入给大模型的上下文片段,而不是一股脑把所有 system prompt 塞进去。
但官方文档写得……怎么说呢,像是给已经懂的人看的。我花了两天才把动态上下文从 demo 跑到真实项目里,这篇文章把我踩过的坑和最终方案全部整理出来。
先说结论
| 配置方式 | 适用场景 | 复杂度 | 我的评价 |
|---|---|---|---|
| 静态 context 注入 | 简单问答 Bot | ⭐ | 够用但不灵活 |
| 基于 Skill 的条件上下文 | 多轮对话、意图路由 | ⭐⭐⭐ | 推荐,性价比最高 |
| 自定义 ContextProvider | 复杂业务逻辑、RAG 融合 | ⭐⭐⭐⭐ | 灵活但需要写不少胶水代码 |
大多数场景用第二种就够了,第三种适合你已经有 RAG pipeline 想跟 OpenClaw 对接的情况。
环境准备
先确保你本地环境没问题:
# Python 3.11+,OpenClaw 0.9.xpipinstallopenclaw>=0.9.0 pipinstallopenai# OpenClaw 底层走 OpenAI 兼容协议OpenClaw 的核心设计思路是:Skill = 上下文 + 工具 + 路由规则的封装单元。动态上下文本质上是在 Skill 层面控制「什么时候给模型看什么信息」。
你还需要一个能调大模型的 API。OpenClaw 兼容 OpenAI 协议,所以任何兼容 OpenAI 格式的 API 都能用。我这里用的是 ofox.ai 的聚合接口,一个 Key 能切换 GPT-5.5、Claude Sonnet 4.6、DeepSeek V4 这些模型,省得每家单独配。
方案一:基于 Skill 的条件上下文(推荐)
最实用的方案。核心思路:定义多个 Skill,每个 Skill 携带自己的上下文片段,OpenClaw 的路由器根据用户意图自动选择激活哪个 Skill。
直接上完整代码:
fromopenclawimportEngine,Skill,ContextBlock,RouterfromopenaiimportOpenAI# 1. 初始化 LLM 客户端client=OpenAI(api_key="your-ofox-key",base_url="https://api.ofox.ai/v1"# 聚合接口,一个 Key 调所有模型)# 2. 定义上下文块product_context=ContextBlock(name="product_info",content=""" 我们的产品是一个智能日程管理工具,支持自然语言创建日程、 跨时区同步、团队协作。定价:免费版 3 个日历,Pro 版 ¥29/月。 """,priority=1# 优先级,冲突时高优先级覆盖)tech_context=ContextBlock(name="tech_faq",content=""" 常见问题: Q: API 返回 401?A: 检查 token 是否过期,有效期 30 天。 Q: 日历同步延迟?A: 检查 webhook 配置,确保回调地址可达。 Q: 支持哪些日历?A: Google Calendar, Outlook, Apple Calendar。 """,priority=1)chat_context=ContextBlock(name="brand_persona",content="你是「小日历」的客服助手,语气友好专业,回答简洁。",priority=0# 基础人设,优先级最低,始终生效)# 3. 定义 Skillsproduct_skill=Skill(name="product_consult",description="处理产品功能、定价、对比相关的问题",contexts=[chat_context,product_context],# 组合多个上下文model="deepseek-v4",# 产品咨询用 DeepSeek V4,便宜够用temperature=0.3)tech_skill=Skill(name="tech_support",description="处理技术问题、报错、API 使用相关的问题",contexts=[chat_context,tech_context],model="claude-sonnet-4.6",# 技术问题用 Claude,推理更准temperature=0.1)fallback_skill=Skill(name="general_chat",description="处理闲聊、打招呼、无法分类的问题",contexts=[chat_context],model="deepseek-v4",temperature=0.7)# 4. 创建引擎engine=Engine(client=client,router=Router(skills=[product_skill,tech_skill,fallback_skill],routing_model="gpt-5.5",# 路由判断用 GPT-5.5,准确率高))# 5. 跑起来response=engine.chat("你们 Pro 版支持多少个日历?")print(response.content)print(f"命中 Skill:{response.matched_skill}")print(f"实际注入上下文:{[c.nameforcinresponse.active_contexts]}")运行结果:
Pro 版支持无限个日历,同时支持跨时区同步和团队协作,每月 29 元。 命中 Skill: product_consult 实际注入上下文: ['brand_persona', 'product_info']关键点在于contexts是个列表,OpenClaw 会按 priority 排序后拼接成最终的 system prompt。低优先级的上下文块(比如品牌人设)会始终保留,高优先级的按 Skill 路由动态切换。
方案二:自定义 ContextProvider(进阶)
如果你已经有 RAG 系统,或者上下文需要实时从数据库/API 拉取,就得用 ContextProvider。
fromopenclawimportContextProvider,ContextBlockimporthttpxclassRAGContextProvider(ContextProvider):"""从向量数据库动态拉取相关文档作为上下文"""def__init__(self,retriever_url:str):self.retriever_url=retriever_urlasyncdefresolve(self,query:str,skill_name:str,history:list)->list[ContextBlock]:# 根据用户问题去 RAG 检索asyncwithhttpx.AsyncClient()asclient:resp=awaitclient.post(self.retriever_url,json={"query":query,"top_k":3})docs=resp.json()["documents"]# 把检索结果包装成 ContextBlockblocks=[]fori,docinenumerate(docs):blocks.append(ContextBlock(name=f"rag_doc_{i}",content=doc["text"],priority=2,# RAG 结果优先级高于静态上下文metadata={"source":doc["source"],"score":doc["score"]}))returnblocks# 使用rag_provider=RAGContextProvider(retriever_url="http://localhost:8000/retrieve")tech_skill_v2=Skill(name="tech_support_v2",description="处理技术问题",contexts=[chat_context],# 静态上下文context_providers=[rag_provider],# 动态上下文,运行时解析model="claude-sonnet-4.6",temperature=0.1)ContextProvider的resolve方法在每次对话时都会被调用,返回的ContextBlock会和静态 contexts 合并后按 priority 排序。
踩坑记录
这部分是我花时间最多的地方,记下来希望后面的人少走弯路。
坑 1:priority 冲突导致上下文被截断
一开始我把所有 ContextBlock 的 priority 都设成 1,结果当总 token 超过模型上下文窗口时,OpenClaw 会按 priority 从低到高删除上下文块。同优先级的情况下,删除顺序是不确定的,导致有时候品牌人设被删了,有时候产品文档被删了,输出很不稳定。
解决办法:给不同类型的上下文设不同的 priority。我的经验是:
priority=0:基础人设,最后才被删priority=1:业务文档priority=2:RAG 检索结果(最新最相关,但也最容易过时)
坑 2:Router 的 routing_model 别用太便宜的模型
我一开始为了省钱,routing_model 用的 DeepSeek V4,结果路由准确率只有 70% 左右,经常把技术问题路由到产品咨询 Skill。换成 GPT-5.5 之后准确率到了 95%+。路由判断的 token 消耗很少(通常不到 200 tokens),别在这省钱。
坑 3:context_providers 是异步的,别忘了 await
如果你在同步代码里调用engine.chat(),内部会自动处理异步。但如果你自己写了异步的 ContextProvider 然后在同步上下文里手动调resolve(),会拿到一个 coroutine 对象而不是结果。报错信息还特别不明显,就是上下文为空,模型回答得莫名其妙。
# ❌ 错误blocks=provider.resolve(query,skill_name,history)# 拿到 coroutine# ✅ 正确importasyncio blocks=asyncio.run(provider.resolve(query,skill_name,history))# ✅ 或者直接用 asyncasyncdefmain():blocks=awaitprovider.resolve(query,skill_name,history)坑 4:ContextBlock 的 content 别太长
单个 ContextBlock 超过 2000 tokens 时,OpenClaw 内部的拼接逻辑会变慢(它会做一次 token 计数来判断是否需要裁剪)。建议把长文档拆成多个小的 ContextBlock,既能精细控制优先级,裁剪时也更合理。
小结
OpenClaw 的动态上下文配置核心就两件事:
- 用 Skill + ContextBlock 做静态编排——大多数场景够用,配置简单
- 用 ContextProvider 做动态注入——适合 RAG 或需要实时数据的场景
这套东西跟具体用哪个大模型无关,只要是 OpenAI 兼容协议的 API 都行。我目前项目里不同 Skill 挂不同模型:路由用 GPT-5.5 保准确率,技术问答用 Claude Sonnet 4.6 保推理质量,闲聊和简单查询用 DeepSeek V4 省成本。ofox.ai 是一个 AI 模型聚合平台,一个 API Key 可以调用 GPT-5.5、Claude Sonnet 4.6、DeepSeek V4 等 50+ 模型,改个 base_url 就行,不用每家单独注册和管理密钥,在这种多模型混用的场景下确实方便。
OpenClaw 迭代很快,文档跟不上代码是常态,建议直接看源码里的examples/目录,比官方文档靠谱多了。
