AI Chat 的上下文工程:别再把聊天框当 textarea

做 AI Chat 产品时,第一版往往长得很像:一个输入框、一个消息列表、一个模型下拉框。这个形态容易让团队误判,以为核心工作只是“把用户输入发给模型,再把回复流式渲染出来”。

真正进入可用阶段后,问题会马上变复杂:用户上传了文件,模型该看到全文还是摘要?用户昨天聊过的项目,今天要不要自动带入?工具执行失败后,下轮对话该不该知道失败原因?同一段上下文既要给模型用,又要给前端展示,还要能计费、审计、导出。

这就是 AI Chat 上下文工程。聊天框只是入口,产品真正要做的是把消息、文件、工具结果、记忆和预算约束组织成一份“模型可以执行、用户可以理解、系统可以追踪”的上下文包。

1. 消息上下文:历史消息不是越多越好

最常见的错误是把最近 N 条消息直接塞回请求。它短期能跑,但会带来三个问题:

  • 噪声累积:用户一次闲聊、一次纠错、一次跑题,都会被下一轮继承。
  • 意图漂移:模型会把旧任务当成当前任务的一部分。
  • 成本失控:上下文窗口越大,延迟、费用和失败概率都会上升。

更稳的做法是把消息分成四类:

类型 是否进入模型上下文 保存目的 例子
当前意图 必进 驱动本轮回答 “把这份 PDF 总结成提纲”
任务约束 条件进入 保持一致性 “输出中文,不要表格”
历史结论 摘要进入 延续项目状态 “上次选了方案 B”
闲聊/废话 默认不进 只做 UI 展示 “好的,谢谢”

这件事听起来像后端策略,其实前端也必须参与。消息组件如果只知道 role/content,很难表达“这条消息是展示记录,还是可复用上下文”。更合理的数据结构至少要有 context_policy

type ChatMessage = {
  id: string;
  role: "user" | "assistant" | "tool";
  content: string;
  createdAt: string;
  contextPolicy: "always" | "summary" | "on_demand" | "display_only";
  source?: "typed" | "file" | "tool" | "memory";
};

这不是过度设计。只要产品支持文件、工具、项目、长对话,其中一个字段的缺失都会在后面变成“为什么模型老是记错”的用户抱怨。

2. 文件上下文:上传成功不等于上下文成功

用户上传文件后,很多系统会走向两个极端:要么直接把全文塞给模型,要么只存一个文件链接让模型“自己理解”。前者成本高,后者模型根本看不到内容。

文件上下文要分三层:

  1. 原始资产:文件本体、大小、类型、权限、过期策略。
  2. 可检索表示:分段、摘要、标题、页码、embedding 或关键词索引。
  3. 本轮引用:这次任务真正需要的片段,以及为什么选中这些片段。

对于 AI 工作区,第三层最关键。用户问“这份合同有什么风险”时,模型需要的是条款片段和引用位置;用户问“把论文改成答辩 PPT”时,模型需要的是章节结构、核心结论和图表说明。两个任务用同一个文件,但上下文包完全不同。

一个实用原则:上传后的第一步不是“问模型”,而是“建立文件目录卡”。例如:

{
  "asset_id": "file_123",
  "kind": "pdf",
  "derived_context": {
    "outline": ["摘要", "方法", "实验", "结论"],
    "key_entities": ["多模型路由", "成本控制", "失败重试"],
    "retrieval_units": 42
  },
  "permission": "owner_only"
}

这张目录卡不替代原文,但能让系统在每轮对话前先判断:要不要检索、检索哪一段、返回给模型多少内容。

3. 工具上下文:工具结果不是聊天记录

AI Chat 产品一旦接入联网搜索、代码执行、图片生成、文档导出,就会遇到工具上下文问题。很多实现会把工具调用结果直接追加成一条消息,这会让聊天记录越来越像日志文件。

更好的边界是:工具结果先进入“任务状态”,再按需要投影到消息流。

type ToolResult = {
  runId: string;
  tool: "web_search" | "file_parse" | "ppt_export" | "image_generate";
  status: "success" | "failed" | "partial";
  userVisibleSummary: string;
  modelContext: string;
  artifacts?: Array<{ name: string; url: string; mime: string }>;
};

这里有一个容易被忽略的点:userVisibleSummarymodelContext 不应该总是相同。用户需要看的是“发生了什么、产物在哪”;模型下一轮需要的是“哪些事实已经确定、哪些约束必须继承”。

如果这两者混在一起,UI 会变啰嗦,模型也会被无关日志污染。

4. 记忆与工作区:跨会话状态要有刹车

很多团队一听到 memory,就想把所有用户偏好都存起来。这个方向危险。记忆的价值不在数量,而在可撤销、可解释、可作用域化。

我更建议把记忆拆成三类:

  • 用户偏好:语言、格式、常用风格。
  • 项目事实:某个项目的技术栈、目标、命名约定。
  • 临时任务状态:本次任务已经完成什么、下一步是什么。

前两类可以跨会话,第三类应该有明确生命周期。否则用户一次临时要求“这次用英文”会变成系统永久偏好;一次测试项目名会污染所有后续对话。

AI 工作区的关键不是“记住一切”,而是让用户知道系统记住了什么、为什么使用、如何删除。

5. 普通 Chat UI 和 AI 工作区的差异

维度 普通 Chat UI AI 工作区
输入 单条文本 文本 + 文件 + 项目 + 工具选择
历史 最近消息 可筛选、可摘要、可作用域化的上下文
文件 附件展示 文件目录卡 + 检索片段 + 引用
工具 调用日志 任务状态 + 可复用产物
记忆 隐式保存 可解释、可撤销、按项目隔离
成本 请求级统计 上下文预算 + 模型选择策略

这个表的核心结论是:AI Chat 的前端架构不能只围绕 message list 设计,而要围绕 context assembly 设计。

6. 一个最小可落地的上下文包

如果你正在做一个 AI Chat 产品,可以先从这个结构开始:

type ContextPacket = {
  intent: {
    rawInput: string;
    normalizedTask: string;
  };
  messages: Array<{
    id: string;
    role: string;
    content: string;
    reason: "current" | "constraint" | "summary";
  }>;
  assets: Array<{
    id: string;
    selectedChunks: string[];
    citationRequired: boolean;
  }>;
  toolState: Array<{
    runId: string;
    status: string;
    modelContext: string;
  }>;
  memory: Array<{
    scope: "user" | "project";
    text: string;
    confidence: number;
  }>;
  budget: {
    maxInputTokens: number;
    preferredModel: string;
  };
};

注意这里没有把所有内容揉成一个 prompt。结构化上下文的好处是,每一部分都能被观察、测试和降级:文件检索失败时可以只回答已知部分;记忆冲突时可以要求确认;预算不足时可以先压缩历史摘要。

7. 常见问题

Q: 小团队需要一开始就做这么完整吗?
A: 不需要。第一版只要把消息、文件、工具结果分开存,就已经避开了最大坑。等到长对话和多文件场景出现,再补检索和记忆。

Q: 上下文工程和 Prompt Engineering 有什么区别?
A: Prompt Engineering 偏“怎么写指令”,上下文工程偏“给模型什么材料、以什么结构给、失败时怎么降级”。后者更接近产品架构。

Q: 为什么这对前端开发者也重要?
A: 因为上下文的很多状态首先出现在前端:用户选择了哪个文件、哪个项目、哪个工具产物、是否要求导出。前端如果只把它们当 UI 状态,后端就很难还原真实意图。

结尾

ChatInOne(chatinone.com)这类 AI chat workspace 的长期价值,不是再做一个“能聊天的页面”,而是把聊天、文件、学习、PPT、导出和项目状态组织到同一个上下文系统里。

如果你也在做 AI 应用,可以先问自己一个问题:你的产品是在保存聊天记录,还是在管理可复用上下文?这两个答案,会导向完全不同的架构。

CSDN 工程落地清单

  • 数据库层:message、asset、tool_run、memory 不要塞进同一张“聊天表”。
  • API 层:每次请求都生成 context packet,并记录组装原因。
  • 前端层:文件选择、项目选择、工具产物选择都要成为可追踪状态。
  • 观测层:记录哪些上下文被使用、哪些被丢弃、哪次回答触发了降级。

这个清单的意义不是增加复杂度,而是让系统从第一天就能解释:模型为什么会这样回答。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐