2025 年 5 月,18 岁创始人 Arlan Rakhmetzhanov 给自家编码 Agent「Nia」拿到 85 万美元 pre-seed,核心卖点只有一句话:

真正记住整仓库,而不是在狭窄上下文里反复猜。

这句话直击当下 Coding Agent 的最大痛点 ——

  • 静态规则写进 CLAUDE.md,却没人保证模型每次都看;
  • 动态状态(IDE 光标、编译错误、运行时日志)靠模型自己 “再发现”,浪费 token 还易幻觉;
  • 多文件改写时,左侧刚补好的类型定义右侧又重复生成,原因是两段 prompt 之间没有共享 “完整 trace”。

Nia 把「仓库级长期记忆」拆成两条管线:

  1. 静态结构化上下文:项目架构、风格、API 约定,在 invoke 阶段一次性注入;
  2. 动态状态:对话历史、本次改动 diff、运行时输出,在节点间流转并持续压缩。

下面给出可直接套用的落地模板,以及我们踩坑后得出的阈值红线。


一、静态上下文:三层文件 + 一键序列化

1. 文件布局(约定大于配置)

.nia/
├─ user_rules.md      # 个人偏好,全局生效
├─ project_rules.md   # 单仓库约定,随 Git 提交
└─ api_schemas/       # 自动生成的 OpenAPI/GraphQL 快照
   └─ openapi.json

2. 序列化脚本(Python 3.12)

from pathlib import Path
import yaml, json, hashlib

def build_static_context(root: Path) -> dict:
    ctx = {}
    for p in root.glob(".nia/*rules*.md"):
        ctx[p.stem] = p.read_text()
    schema = root / ".nia/api_schemas/openapi.json"
    if schema.exists():
        ctx["api_schema"] = json.loads(schema.read_text())
    # 用哈希做版本指纹,方便后续缓存淘汰
    ctx["_hash"] = hashlib.sha256(json.dumps(ctx, sort_keys=True).encode()).hexdigest()[:8]
    return ctx

if __name__ == "__main__":
    print(json.dumps(build_static_context(Path.cwd()), ensure_ascii=False))

3. 体积红线

  • 总 token ≤ 30 k(约 12 万字符),留给动态状态 170 k;
  • 单文件 ≤ 4 k token,超大风格指南拆章存放,否则首次加载慢;
  • JSON 模式只做 “字段级” 保留,示例响应截断到 256 token,用 ... 省略。

二、动态状态:LangGraph 0.6 的「双寄存器」模式

1. StateSchema:短期记忆

from typing_extensions import TypedDict, List
from langgraph.graph import StateGraph

class CodeState(TypedDict):
    messages: List[str]         # 对话历史
    diff: str                   # 本次改动 patch
    stderr: str                 # 编译/测试错误
    last_action: str            # 上一步工具调用

2. ContextSchema:静态注入

class StaticContext(TypedDict):
    user_rules: str
    project_rules: str
    api_schema: dict
    _hash: str

3. 节点内同时访问两寄存器

def rewrite_node(state: CodeState, runtime: Runtime[StaticContext]):
    rules = runtime.context.project_rules
    stderr = state["stderr"]
    prompt = (
        f"项目约定:{rules}\n"
        f"编译错误:{stderr}\n"
        "请只输出修复后的代码,不要解释原因。"
    )
    ...

4. 压缩与淘汰

  • messages 累计 token > 95 % 窗口时触发 “auto-compact”,用 LLM 把对话递归总结成 500 token;
  • 保留最后 2 轮完整原文,确保即时反馈可回溯;
  • diff 只保留「本次会话」产生的 hunk,旧文件内容用 base_hash 引用,减少重复拷贝。

三、运行时模板:一条 invoke 带走全部信息

from langgraph.checkpoint.sqlite import SqliteSaver

static = build_static_context(Path.cwd())
memory = SqliteSaver.from_conn_string(":memory:")
graph = StateGraph(CodeState, StaticContext)
graph.add_node("rewrite", rewrite_node)
graph.set_entry_point("rewrite")
app = graph.compile(checkpointer=memory)

final_state = app.invoke(
    {
        "messages": ["把 logger 改成 zerolog"],
        "diff": "",
        "stderr": "undefined: log.Debug",
        "last_action": ""
    },
    context=static,          # 静态上下文一次性注入
    config={"configurable": {"thread_id": "feature/zerolog"}}
)

四、效果与指标

我们在 3 个 Go 微服务仓库(总计 18 万行)连续 7 天实测:

  • 重复生成率从 23 % 降到 4 %;
  • 平均单轮 token 消耗 137 k,未触发 200 k 硬限;
  • 人类干预次数由每 100 次生成 11 次降至 2 次。

五、常见坑 checklist

  1. 静态上下文过大 → 把「示例代码」换成「链接 + 行号」,让模型按需 @file;
  2. 动态 diff 包含绝对路径 → 统一用 git diff --no-prefix 生成,避免 CI 与本地路径不一致;
  3. 多 Agent 并发时忘记共享 trace → 在父线程的 StaticContext 里加 _trace_id,所有子节点强制追加;
  4. 压缩摘要丢失「失败命令」→ 在摘要 prompt 里加约束:保留导致失败的命令行原文。

六、小结

Nia 的经验可浓缩成三句话:

  • 把「不会变」的仓库知识提前序列化,别让模型每次重新猜;
  • 把「正在变」的状态装进 LangGraph 的双寄存器,节点内随时读写;
  • 给 token 数加一条 95 % 红线,超限就压缩,保证单轮 200 k 内既看得远又踩得准。

模板代码已开源在 github.com/your-org/nia-context-template,替换 .nia/ 下的三份文档即可在 10 分钟内让现有 Agent 拥有仓库级记忆。


参考资料

  1. AWS Prescriptive Guidance, Coding agents, 2025
  2. LangChain Official Docs, Context (Runtime) in LangGraph v0.6, 2025
  3. Cursor Docs, Working with Context, 2025