上下文工程:初创企业的护城河
我们认识的每家创业公司都部署了卓越的上下文策略来构建感觉智能、个性化或防御性强的产品。
最有效的早期阶段团队将上下文工程化,就像基础设施一样。
他们向模型提供正确的知识、正确的约束和正确的记忆,然后在每个新代理、每个新产品中重用这个脚手架。
这是一个竞争对手无法轻易复制的复合优势。
在本指南中,我们将分解智能团队如何使用您已经知道的工具,将原始LLM转化为超出其规模的上下文感知系统。
我们将涵盖:
- 如何将"产品知道什么"外部化到基于文件系统
- 如何使用向量数据库和检索构建持久记忆
- 如何使用黄金示例和约束驱动提示来引导输出
- 以及如何自动化上下文更新,以便每个代理在使用中变得更智能
您将掌握今天可以实现的模式和可随您扩展的上下文栈。

1、专用上下文文件
编码代理通常从仓库中的专用上下文文件开始。
例如,Anthropic的Claude Code推荐在仓库根目录放置一个CLAUDE.md,其中包含简洁的指南,例如常见shell命令、编码风格规则、测试步骤、环境设置等。
每个CLAUDE.md在您在项目中聊天时都会自动注入Claude的上下文。
同样,Google的Gemini CLI支持一个GEMINI.md文件,放置在工作目录(或~/.gemini/)中,为该项目或全局范围共享指令。
在多模型设置中,团队越来越多地使用AGENTS.md规范来统一工具规则。
例如,开放的AGENTS.md格式就像代理的README,列出开发提示和指令。
一个示例AGENTS.md可能包括如下部分:
# 示例 AGENTS.md
## 开发环境提示
- 在每次提交前在每个包根目录运行`npm run build`。
- 使用`git rebase -i main`来压缩修复提交。
- 确保`package.json`中的包名称与仓库结构匹配。
## 测试指令
- 运行`pnpm turbo run test --filter <package>`来测试特定包。
- 始终在本地运行`pnpm lint`和`pnpm test`然后推送。
- 为您添加或修改的任何功能编写新测试。
## PR指令
- PR标题格式:`[<package>] 一个简短的描述性标题`
- 根据需要包含截图或输出示例。
优势是双重的
- 文件与代码版本化
- Agent CLI(Claude、Gemini等)可以自动将其加载为上下文。
在实践中,许多项目为所有模型符号链接或复制一个文件(例如,将GEMINI.md和CLAUDE.md链接到一个单一的AGENTS.md)。
因为CLAUDE.md被预置于每个提示之前,所以必须保持简洁和格式良好。
它甚至可以自动更新。例如,Claude的"#"键让您在中聊天时添加笔记,然后您将其提交回CLAUDE.md。
随着时间的推移,这在代码仓库中构建了持久的"记忆",确保每个新代理会话从所有关键项目知识开始。
2、嵌入式(长期)记忆系统
除了静态文件,许多代理使用向量记忆存储来动态持久化和检索上下文。
典型模式是:将重要文本(用户笔记、文档、日志等)分割成块,嵌入它们,并存储在向量数据库中。
LangChain和LlamaIndex等工具为此提供了抽象。
例如,一个LangChain+Chroma管道可能看起来像加载您的文档(例如PDF或文本文件),将其分割成~500–1000令牌块,使用模型嵌入,并创建一个Chroma向量存储。
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
docs = load_documents() # 您的文档加载器,例如PDF/文本
splitter = RecursiveCharacterTextSplitter(chunk_size=800, overlap=100)
chunks = splitter.split_documents(docs)
db = Chroma.from_documents(chunks, OpenAIEmbeddings(), persist_directory="./chroma_db")
db.persist() # 将嵌入保存到磁盘
一旦构建,您可以在运行时通过计算用户查询的嵌入并进行相似性搜索来查询它。
例如:
query = "PPO如何工作?"
db = Chroma(persist_directory="chroma_db", embedding_function=OpenAIEmbeddings())
results = db.similarity_search_with_relevance_scores(query, k=3)
context = "\n\n---\n\n".join([doc.page_content for doc, _ in results])
这检索到与查询语义相关的顶级3块,然后您将其馈送到提示中。
另一个示例是使用Weaviate在Node/TypeScript代理中,您可能像这样初始化存储并添加文本:
import { WeaviateStore } from "@langchain/weaviate";
const weaviateStore = await WeaviateStore.fromTexts(
["hello world", "hi there", "how are you"], // 文档
[{ foo: "bar" }, { foo: "baz" }, { foo: "qux" }], // 元数据
new OpenAIEmbeddings(),
weaviateClient,
weaviateSchema
);
const res = await weaviateStore.similaritySearch("hello", 1);
关键是一旦存储设置,您就可以嵌入新信息并在代码中查询它,无缝地将外部记忆与您的聊天链接。
对于LlamaIndex,存在类似的方法。
LlamaIndex的Memory类可以精细控制短期记忆与长期记忆。
您可以配置它,以便每次达到令牌限制时,较旧的消息通过"Memory Blocks"存档,只有相关部分被检索。
例如,您可以包含一个VectorMemoryBlock,将聊天批次推送到Chroma存储。
from llama_index.core.memory import Memory, VectorMemoryBlock
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.llms.openai import OpenAIEmbedding
client = chromadb.Client()
vector_store = ChromaVectorStore(
chroma_collection=client.get_or_create_collection("my_collection")
)
blocks = [
VectorMemoryBlock(name="chat_history",
vector_store=vector_store,
embed_model=OpenAIEmbedding(),
priority=2)
]
memory = Memory.from_defaults(
session_id="user123", token_limit=10000,
chat_history_token_ratio=0.2, token_flush_size=100,
memory_blocks=blocks
)
LlamaIndex将在摘要增长太大时自动通过向量存储刷新和检索消息。
这样,只有嵌入的事实和摘要被长期保留。
在真实应用中,您还需要为每个记忆条目定义一个模式(例如,具有role、content、timestamp、source等的JSON)并将其与嵌入一起存储。
更新记忆只需在每次交互后添加新条目。
例如,在处理用户输入和模型输出后,您将db.add_documents([new_chunk])或store.add_texts(...),以便未来召回包括最新回合。
Weaviate的多租户支持甚至可以将每个用户的记忆隔离到自己的"tenant"命名空间中。
随着时间的推移,这为每个用户或团队提供了一个范围化的知识存储。
3、在提示中使用黄金示例(少样本)
另一个强大技术是少样本提示,即在提示中嵌入一个或多个"黄金示例"输入输出对来引导模型。
在LangChain中,您可以构建一个FewShotPromptTemplate来声明式地做到这一点。
examples = [
{"shape": "Square", "sides": "Eight (8)"},
{"shape": "Triangle", "sides": "Six (6)"}
]
example_prompt = PromptTemplate(
input_variables=["shape","sides"],
template="Shape: {shape}\nSides: {sides}\n"
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="Understand the pattern and provide the response for my query.",
suffix="Shape: {shape}",
input_variables=["shape"]
)
final_prompt = few_shot_prompt.format(shape="Pentagon")
print(final_prompt)
这自动创建一个像这样的提示
Understand the pattern and provide the response for my query.
Shape: Square
Sides: Eight (8)
Shape: Triangle
Sides: Six (6)
Shape: Pentagon
然后LLM被期望输出Ten (10),遵循演示的模式。
在实践中,您嵌入领域特定的示例。
对于AI编码代理,"黄金示例"可能是示例用户问题与理想助手答案,或输入输出模式。
注释提示模板(例如使用上述的prefix和suffix)使代理更清楚格式应该是什么。
少样本示例提醒模型期望的行为。它们应该选择覆盖重要情况和约束。(通常2–3个强示例就足够。)
好的提示是具体的、结构化的和格式感知的,少样本示例通过明确演示期望的格式和风格来贡献所有三个方面。
4、约束驱动提示和护栏
提示通常包括严格的输出约束或检查列表。
常见模式是要求JSON或定义的模式输出,以便您可以自动验证它。
代理提示可能以结束:
严格返回遵循指定模式的有效JSON。没有额外键或文本。
现代LLM工具包原生支持这一点。
Databricks的结构化输出让您提供JSON模式,以便模型的解码器在每个步骤都被约束产生有效JSON。
在实践中,即使没有此类API,您也可以在提示中嵌入类似模式的检查列表。
例如:
System: You will output a JSON object. It must have keys "name" (string), "age" (integer), and "email" (string).
Never output any extra fields or narrative.
User: {"query": "Get user info", "id": 123}
Assistant:
这种指导(通常称为模式或输出模板)充当护栏。当正确格式化时,助手可以自动解析和验证。
如果它偏离,您可以检测到。有些团队甚至在提示末尾包括最终"检查列表"如"## 检查列表:您是否… [x] 包括关键字段,[x] 遵守格式,[ ] 等。"并要求模型对每个项目回答是/否。
如果您特别使用Sonnet 4.5或Opus 4.5,需要注意其他细微差别。
5、自动化上下文更新
有效的代理在每次交互后自动更新其上下文,而不仅仅是被动继承历史。
例如,许多聊天代理将每个新用户消息和助手回复附加到对话记忆中(例如LangChain的ConversationBufferMemory)或到一个摘要(例如ConversationSummaryMemory,它提示LLM"逐步总结"最近对话)。
代理使用工具或API后,也应该纳入结果。
具体来说,如果您的代理获取用户数据或调用函数,您然后创建一个"assistant:"消息如"FetchUser returned {name: 'Alice', age:30}"并将其反馈到记忆或提示链中。
这意味着您需要在每一步挂钩,当用户说些什么,或代理采取行动,或外部工具返回数据时,您将那转化为文本并添加到代理的记忆中。
在长会话中,您定期检查点并压缩旧上下文(例如总结最后100条消息)以保持在令牌限制内。
为此,您需要使用自动化工具和系统来维护、压缩和检索代理的上下文,以便代理适应会话增长。
在实践中这是结合短期聊天缓冲与长期存储。立即历史(最后几条消息)直接进入LLM,而较旧内容被嵌入或总结。
6、测试和调试上下文更新
一个简单测试方法是在对话中插入一个"tracer"细节,看代理是否后来回忆它。
您也可以提示代理总结最近回合。
如果它准确重复它们,则上下文被尊重,但如果它求助于通用答案,则上下文可能已被丢弃。
另一个技巧是在记忆的上下文中包括微妙的矛盾,像接地的代理应该标记或纠正它们。
这些检查帮助验证您的记忆系统是否按预期工作。
7、新兴模式和最佳实践
在项目中,我们也看到一些常见的新兴实践:
- 事件驱动刷新:在关键事件(用户消息、API结果、工具输出)上触发更新。例如,每10条消息后调用摘要器,或每当用户改变话题时重新查询向量存储。有些代理框架甚至原生支持函数调用(例如在JSON中指定存在什么工具),以便工具结果作为结构化数据返回模型可以纳入。
- 范围化/命名空间记忆:按用户、团队或项目隔离记忆。这避免了交叉对话。例如,Weaviate的多租户让您将每个用户的文档存储在单独的"tenant"中,以便查询从未混合用户。同样,您可以按用户ID分区Chroma或LlamaIndex存储。这反映了真实应用中每个用户的长期记忆是私有的。
- 结构化提示调试:将提示分解为块(例如系统、用户、工具、记忆)并记录它们。有些团队使用元代理来测试提示,其中一个代理生成上下文,另一个验证输出格式。
- 混合记忆(向量+文档):使用向量存储用于非结构化块,但也维护结构化数据库用于明确事实(用户配置文件、配置设置等)。您可以将Redis或MongoDB与向量DB结合,用于短期/会话数据与语义记忆。这种混合模式让您对已知字段进行快速查找,对长文本进行相似性搜索。
8、下一步是什么?
我们预见上下文管理周围会有更多自动化。在不久的将来,代理框架将基于内容自动标记和分块新记忆(使用向量DB流式摄取),或摘要器并行运行而无需开发者手动调用。
"通用上下文管理器"或标准化记忆API的想法正在获得 traction。
未来可能持有更紧密的集成(例如跨代理记忆共享、实时知识图、OS级上下文信号)但上述实践代表了今天实用的尖端。
我们将继续关注这个空间的演变。
原文链接:How Early-Stage Startups Turn CONTEXT into Their MOAT
汇智网翻译整理,转载请标明出处