专题目录

Memory Center:AI 编程上下文与记忆系统工程实践
问题与设计思想 5
  1. 01为什么 AI 编程助手需要一个项目记忆中心
  2. 02上下文工程:如何提供下一步真正需要的信息
  3. 03事实、推断和未知:为 AI 的工程判断划定边界
  4. 04代码、文档、任务和长期知识应该如何分工
  5. 05从设计原则到架构约束:如何记录真正有用的 ADR
服务架构与项目边界 5
  1. 06从单项目工具演进为多项目常驻服务
  2. 07管理 API 与 MCP 如何共用应用层
  3. 08FastAPI 与 MCP 如何共同管理服务生命周期
  4. 09项目名称和目录为什么不应该成为项目身份
  5. 10本机模式与令牌模式:多项目访问边界怎么设计
索引与代码事实 5
  1. 11一份源码如何变成可查询的工程事实
  2. 12用 Tree-sitter 理解多语言代码的结构与局限
  3. 13如何用节点和边表达类、方法、接口与服务
  4. 14Java 与 Spring 的跨服务关系为什么需要专用分析
  5. 15增量索引如何复用旧事实,又避免复用损坏结果
检索与证据组合 5
  1. 16代码和文档怎样分块,才能保留可追溯来源
  2. 17BM25 如何把关键词匹配变成相关性排序
  3. 18向量检索如何处理模型档案、范围与缓存
  4. 19混合检索:召回、RRF 与重排如何配合
  5. 20如何返回可信证据:权限、新鲜度与覆盖范围
任务记忆与长期知识 5
  1. 21开始、恢复和结束:给 AI 任务设计完整生命周期
  2. 22项目记忆为什么不能只是追加一段文本
  3. 23Wiki 为什么需要稳定记录、修订与冲突检测
  4. 24从任务经验到长期知识:如何控制晋升过程
  5. 25从文档到 Skill:可复用经验如何形成工件
可靠后台任务 5
  1. 26为什么索引任务需要独立 Worker
  2. 27一个后台任务的排队、执行、取消与中断恢复
  3. 28新索引构建失败,怎样保住旧索引的查询能力
  4. 29在途任务遇到配置变更,如何拒绝过期结果
  5. 30如何协调项目并发、文件 Worker 与外部模型请求
存储与运维治理 5
  1. 31SQLite 与 PostgreSQL:根据负载选择存储形态
  2. 32SQLite 流式 Top-K 与 pgvector 距离检索
  3. 33项目空间统计为什么不等于实际磁盘占用
  4. 34历史索引清理:从直接删除到预览与复核
  5. 35如何部署与维护一个本地优先的 AI 服务
验证与完整实践 5
  1. 36MCP 调用为什么需要分阶段执行轨迹
  2. 37如何测试 MCP、HTTP 与应用层的共同契约
  3. 38检索效果怎样评估,哪些指标容易误导
  4. 39一次实际开发任务如何使用 Memory Center
  5. 40复盘:哪些设计值得保留,哪些需要重新判断

第 2 篇

工程技术

上下文工程:如何提供下一步真正需要的信息

结合实际开发项目,讨论上下文工程:如何提供下一步真正需要的信息,说明设计理由、关键源码、验证方法及适用边界。

起源未来约 7 分钟阅读#Memory Center#问题与设计思想#工程实践
我的阅读
Memory Center 问题与设计思想 工程实践

本文目录 · 8 节

本文属于 Memory Center:AI 编程上下文与记忆系统工程实践,第 02 篇,位于“问题与设计思想”模块。案例背景统一采用实际开发项目;复现步骤使用脱敏教学样例,不代表特定生产事故。

从实际问题出发

实际开发项目有大量代码和历史说明。把它们全部交给模型,看似资料充分,却会让最新接口约束与过期讨论竞争注意力。真正有用的上下文,是能改变当前决策且能核查来源的信息。

按决策组织信息

先明确当前是在定位代码、分析影响,还是恢复任务。不同任务需要不同证据:定位需要路径和符号,分析需要关系和覆盖,恢复需要已做事项与未决问题。上下文质量不能只用字数评价。

上下文工程:如何提供下一步真正需要的信息:设计链路示意

图中展示的是本篇关注的职责和顺序。正常流程之外,还要结合下面的源码与实验检查失败分支;它不是对某次线上调用的运行时追踪。

渐进读取的成本与收益

第一次返回有限命中与来源,必要时通过引用继续展开。它用额外一次交互换取更少的无关材料,也让调用方有机会判断是否值得继续读取。限制响应体积不代表服务能知道宿主剩余上下文,更不代表它自动压缩宿主会话。

把上下文预算写成选择问题

假设一次修改只需要接口定义、两个消费者和一条约束。把整个目录装进会话,并不会让缺失的消费者自动出现。可以为每条候选材料追问:它会改变哪一步决策?是否能先返回来源再展开?它是否比当前源码更旧?

因此预算不仅是截断字符数,还包含选择与排序。截断应保留可见标记;如果关键证据没有进入范围,结果要表达不足,而不是让模型自行补齐。

关键源码与阅读方法

源码基线为 Memory Center 6e90553(文档标注 5.0.0b24)。定位:memory_center/platform/application.py,函数 lookup,起始行 45。

以下为源码节选,省略后续分支;不能作为独立脚本执行。

    def lookup(self, actor, workspace_id, query, mode='hybrid', limit=10, session_id=''):
        workspace = self.access.scope(actor, workspace_id)
        if session_id:
            session = self.access.owned(actor, 'session', session_id, workspace_id)
            if session['status'] != 'active':
                raise CatalogError('SESSION_CLOSED', '会话已关闭')
        if mode in {'symbol', 'callers', 'callees', 'context'}:
            from memory_center.core.orchestrator.query import (
                memory_callees,
                memory_callers,
                memory_context,
                memory_symbol,
            )
            _, index = self.indexed(actor, workspace_id)
            methods = {'symbol': memory_symbol, 'callers': memory_callers, 'callees': memory_callees, 'context': memory_context}
            generic = query_code_graph(index['workspace_file'], query, mode, limit)
            if mode == 'context' and generic and any(s['language'] == 'java' for s in generic['symbols']):
                generic = None
            if generic is not None:
                result = generic
                for symbol in [*result.get('symbols', []), *[e['symbol'] for e in result.get('chain', [])]]:
                    symbol['freshness'] = 'CURRENT' if symbol_fresh(workspace['root'], symbol) else 'STALE'
            elif mode in {'symbol', 'callers', 'callees'}:
                result = methods[mode](index['workspace_file'], query, limit=min(limit, 50), view='minimal')
            else:
                result = memory_context(index['workspace_file'], query, view='minimal')
            result['workspace_id'] = workspace_id
            result['generation'] = index['generation']
        else:
            result = self.retrieval.search(workspace, actor, query, mode, limit)
            # 源码命中实时核对哈希,只核对有界候选,不重建索引。
            for item in result.get('items', []):
                source = item['source']
                if source['kind'] == 'code':
                    path = Path(workspace['root']) / source['path']
                    try:

阅读时先找输入由谁提供,再区分纯计算、读取和写入;遇到异常或提前返回,追问调用方会得到什么,以及已有状态是否仍然有效。上面的代码只证明展示范围内的实现,跨模块行为应继续查看调用处和相应测试。

用一个小实验核对设计

建议在独立临时目录和隔离数据中操作,使用虚构的接口、文档与项目名称。

  1. 准备含过期文档的示例目录。
  2. 分别提出定位与恢复问题。
  3. 记录每条返回信息具体支持哪一步判断。

**要核对的结果:**如果删掉一段材料不影响任何决策,它可能不该默认进入上下文。

可把观察记录为下表;没有执行的项写“未执行”,不能用设计预期冒充实测结果。

核对项 应保留的证据
使用条件 源码版本、配置、输入与项目范围
正常行为 实际返回、来源位置或状态变化
关键失败分支 错误码、可见结果与原状态是否保留
结论范围 已覆盖、未覆盖和需要进一步验证的条件

适用边界与继续阅读

本篇以静态源码与项目设计为依据,给出可执行的核对方法,未据此宣称已经完成新的性能或生产环境测试。不同版本的入口、默认值和底层实现可能变化,应先核对当前合同,再把方法用于自己的实际开发项目。

理解这一节后,可以回到专题总导航,沿同一模块继续阅读;顺序学习时使用专栏内的上一篇和下一篇。涉及具体技术选择,应连同本篇的代价与限制一起采用。