上下文工程 vs 提示词工程:区别与落地路径

你团队里那位最会写 prompt 的同事,可能正在用“提示词工程”的方法,去解决一个本应由“上下文工程”负责的问题:系统不是“不会说”,而是“没看到该看的东西”。

副标题:Agent 与上下文工程 · 选型与落地

本文为 vergex.cn 原创首发,转载请注明出处。

你团队里那位最会写 prompt 的同事,可能正在用“提示词工程”的方法,去解决一个本应由“上下文工程”负责的问题:系统不是“不会说”,而是“没看到该看的东西”。

一、先对齐概念:两者不是“同一个东西的两套叫法”

提示词工程(Prompt Engineering)优化的是单次请求里模型“看到”的指令本身——怎么措辞、加多少示例、限定什么格式、如何约束输出。上下文工程(Context Engineering)优化的是进入模型窗口之前的全部信息:哪些来源值得被带进来、以什么结构组织、占多少 token、失败后如何降级、答案错了怎么追溯。

我经常用下面这张表向技术委员会解释资源分配。表中的评估维度可直接复用到内部评审,完整权重和打分项的完整版已在 VergeX 站点放出。

维度 提示词工程 上下文工程
优化对象 单条/单次指令的语义表达 进入模型窗口的全部信息结构与来源
核心手段 指令模板、少样本示例、角色设定、输出格式 召回策略、分块边界、元数据、预算分配、失效与版本
成功标准 单次回复质量(相关性、格式、幻觉率) 端到端任务完成率、上下文命中率、token 利用率、可追溯性
组织职责 算法/产品工程师 平台/数据/Agent 架构团队
迭代周期 小时级 周/月级,随业务知识库变化

关键结论是:提示词工程解决“表达问题”,上下文工程解决“输入供给问题”。 当团队反复调 prompt 却效果停滞时,大概率是上下文供给没做好,而不是 prompt 写得不够“魔法”。

二、上下文工程的三层操作对象

我把上下文工程拆成三层,每层对应不同的落地抓手。

来源层(Source Layer) 决定“什么东西有资格进入上下文”。不是知识库越大越好,而是要建立召回、过滤、排序的闭环。常见做法包括:按业务实体做索引隔离,避免把 A 产品的文档塞进 B 产品的对话;给每个 chunk 打上版本、生效时间、权限标签;在召回后做二次重排(rerank),而不是直接取 top-k。

结构层(Structure Layer) 决定“进来后怎么呈现”。模型对边界标记非常敏感。推荐用带显式标签的容器结构,例如 <source id="..." title="..."> 包裹每个知识片段,而不是用换行或 Markdown 列表简单拼接。结构层还要处理冲突:当两个来源结论矛盾时,是通过元数据标注优先级,还是在 prompt 里让模型自行判断?这需要在工程层面显式定义策略。

运行层(Runtime Layer) 决定“窗口预算怎么花”。总窗口不等于可用上下文长度,必须预留生成 token、系统指令开销、少量安全余量。运行层还要处理失效:召回为空怎么办、上下文超长时先丢哪一类信息、模型输出要求引用但来源缺失时如何兜底。

三、提示词工程的边界在哪里

提示词工程在三种场景下回报率最高:一是任务语义明确、但需要约束输出形式;二是通过 few-shot 让模型快速对齐隐式风格;三是设置角色和拒绝策略,降低安全与合规风险。

但它有明显边界。当知识不在模型参数里、且会动态变化时,仅靠 prompt 调优无法解决。 比如客服 Agent 需要引用昨日刚更新的退款政策,你再怎么优化措辞,模型也拿不到这条信息。此时问题的根因是上下文供给链路,而不是 prompt 表达。

另一个边界是“上下文噪声”。如果一次请求里塞进了 20 个不相关 chunk,再好的 prompt 也难以让模型忽略它们。LLM 对位置有显著偏好,中间内容容易被“丢失”。这种情况下,与其继续打磨 prompt,不如先优化召回与重排。

判断何时该从 prompt 工程切换到上下文工程,有一个简单标准:连续三轮 prompt 调优后,端到端指标提升不到 5%,就把精力转向上下文管线。

四、从“单轮调优”到“上下文管线”的落地路径

L2 管理者推动这件事,建议分四步走,不要一开始就重写整个 Agent。

第一步,建立无上下文的 prompt 基线。 用固定知识写死 prompt,确认模型在信息完备时的表现天花板。这一步的输出是“理论上最好能做到什么程度”,也是后续所有对比的 benchmark。

第二步,接入最小可用上下文。 引入向量检索或规则召回,把 top-3 到 top-5 的 chunk 注入对话。此时先不要追求复杂策略,重点验证:加了上下文后,答案是否真的在引用这些来源,而不是用参数记忆胡编。

第三步,做动态预算与重排。 根据问题类型分配不同上下文长度:事实性问题给短而精的 chunk,复杂分析任务给长上下文。引入 rerank 模型或规则打分,把真正相关的片段提前。这一步开始需要 token 成本核算。

第四步,建立评估飞轮。 用固定测试集持续跑端到端指标,包括答案正确率、来源引用命中率、上下文 token 利用率。推荐把核心用例写成 pytest,纳入 CI:


python -m pytest tests/context_pipeline/ --tb=short -q

这个飞轮跑起来后,prompt 工程和上下文工程就可以并行迭代,而不是互相甩锅。

五、一个可复现的上下文注入评估脚本

下面这段代码可以直接跑,用来评估“上下文装了多少、还剩多少预算、是否发生截断”。它使用 tiktoken 做 token 计数,用 openai 的 SDK 发起请求。你需要先安装依赖并设置 OPENAI_API_KEY:


pip install openai tiktoken
export OPENAI_API_KEY="sk-..."

import os
import tiktoken
from openai import OpenAI

MODEL = "gpt-4o"
ENCODING = tiktoken.encoding_for_model(MODEL)
MAX_CONTEXT = 128_000
RESERVE_GENERATION = 4_096
RESERVE_SYSTEM_OVERHEAD = 1_024


def token_count(text: str) -> int:
    return len(ENCODING.encode(text, disallowed_special=()))


def assemble_context(retrieved_chunks, query, instructions):
    system_prompt = f"""You are a strict Q/A assistant.
{instructions}
Use ONLY the <source> chunks below to answer. Cite using [id]."""

    context_block = "\n\n".join(
        f'<source id="{c["id"]}" title="{c["title"]}">\n{c["text"]}\n</source>'
        for c in retrieved_chunks
    )

    user_prompt = (
        f"<question>\n{query}\n</question>\n\n"
        f"<context>\n{context_block}\n</context>"
    )
    return system_prompt, user_prompt


def fit_context(system_prompt, user_prompt, max_context_tokens):
    system_tokens = token_count(system_prompt)
    user_tokens = token_count(user_prompt)
    total = system_tokens + user_tokens + RESERVE_SYSTEM_OVERHEAD

    if total > max_context_tokens:
        allowed_user = max_context_tokens - system_tokens - RESERVE_SYSTEM_OVERHEAD
        encoded = ENCODING.encode(user_prompt, disallowed_special=())
        truncated = ENCODING.decode(encoded[:allowed_user])
        user_prompt = truncated + "\n...[truncated]"
        total = system_tokens + token_count(user_prompt) + RESERVE_SYSTEM_OVERHEAD

    return system_prompt, user_prompt, total


def run(system_prompt, user_prompt):
    client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt},
        ],
        temperature=0.1,
        max_tokens=RESERVE_GENERATION,
    )
    return response.choices[0].message.content


if __name__ == "__main__":
    chunks = [
        {"id": "doc-1", "title": "退款政策", "text": "用户可在 7 天内申请无理由退款。"},
        {"id": "doc-2", "title": "VIP 退款", "text": "VIP 用户享有 14 天超长退款期。"},
    ]

    sys_p, user_p = assemble_context(
        chunks,
        query="VIP 退款期限是多久?",
        instructions="Answer in Chinese. If the answer is not in the context, say '未找到依据'."
    )

    sys_p, user_p, total = fit_context(
        sys_p, user_p, MAX_CONTEXT - RESERVE_GENERATION
    )

    budget_left = MAX_CONTEXT - total - RESERVE_GENERATION
    print(f"上下文占用 token: {total}")
    print(f"剩余预算(含生成预留): {budget_left}")

    # 取消注释下一行即可真正调用模型
    # print(run(sys_p, user_p))

这个脚本的核心价值不在“能跑”,而在它把上下文预算算清楚了。总窗口减去生成预留和系统开销,才是真正能分给上下文的部分。 很多项目的问题不是 prompt 写得不好,而是上下文把窗口塞太满,导致模型没空间组织答案。

六、两个最常见的“隐形坑”

坑一:直接把召回的 chunk 拼进 messages,不加来源标签和边界。 我见过不少团队把检索结果用 "\n---\n".join(chunks) 丢进 user message,然后要求模型“给出引用”。结果模型要么杜撰引用编号,要么把多个来源混在一起张冠李戴。原因是模型无法可靠地区分“这是来源 A 的文本”和“这是来源 B 的文本”。解法很简单:给每个 chunk 加显式标签,例如 <source id="doc-2">...</source>,并在 prompt 里规定引用格式。没有结构化边界,就不要要求结构化引用。

坑二:把上下文窗口填到 100%,没有预留生成 token 和系统开销。 有些同学看到模型支持 128K 上下文,就以为可以塞 128K 的文本。实际上,系统指令、角色设定、输出格式示例都会占 token;模型回复本身也需要预留空间。如果你在运行时把上下文塞得太满,API 会触发截断,或者模型在末尾被迫中断。建议用上面的脚本算清楚:可用上下文长度 = 模型总窗口 - 最大生成长度 - 系统开销 - 10% 安全余量。宁可少塞一点,也要保证生成空间完整。

这两个坑之所以“隐形”,是因为它们不会直接报错,只会表现为“模型偶尔答错”“引用不稳定”“长文本回答被截断”,从而让人误以为要继续调 prompt。

七、给 L2 管理者的落地清单

如果你是技术管理者,想要推动团队从 prompt 工程走向上下文工程,建议先检查下面几项:

  • 团队是否有统一的上下文注入规范,包括 chunk 边界、元数据字段、引用格式?
  • 每次请求是否有明确的 token 预算分配策略,而不是“能塞多少塞多少”?
  • 是否建立了端到端评估集,覆盖“上下文命中”“来源引用正确”“无上下文时拒答”三类用例?
  • prompt 迭代和上下文迭代是否由不同角色负责,还是全压在一个人身上?
  • 生产环境是否对上下文来源做了版本和生效时间管理,避免新旧政策冲突?

文末再提一句:前面那张对照表只是简化版,包含完整评分权重、检查项和部门分工的《上下文工程 vs 提示词工程 评估对照表》完整版已放在 VergeX 站点,可直接拿来开会用。

反模式清单:

  • 让业务团队直接在 prompt 里拼接长文本,而不是走上下文管线。
  • 用离线 prompt 评测分数代替端到端任务完成率。
  • 把上下文窗口填满后再交给模型,不预留生成 token。
  • 要求模型给出引用,却不给 chunk 标注来源。
  • 不同环境使用不同分块策略,却不做回归测试。
  • 把动态知识更新当作 prompt 调优问题处理。

核心总结:提示词工程决定单次交互能有多好,上下文工程决定这个好能被规模化、可复现地交付多少次。

技术深度解析

5 个即拿即用的 Agent 工作流

2026-10-22 20:18:29

技术深度解析

vLLM 部署踩坑全记录:显存、并发与常见报错

2026-10-22 20:18:35

0 条回复 A文章作者 M管理员
VergeX|科技前沿
    暂无讨论,说说你的看法吧
❯
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索