07 · 记忆、可观测与成本控制

对应代码:app/agent/memory.pyapp/observability/tracer.py

这三件事看起来是"锦上添花",实际决定了 Agent 能不能上生产

一、记忆:窗口 + 摘要

问题

多轮对话时如果每轮都把全部历史塞进 Prompt:

  • :token 线性增长
  • :首 token 延迟越来越高
  • :研究显示长上下文存在"中间遗忘"(lost in the middle)

方案

markdown 复制代码
最近 12 轮原文(保证指代可解析)
        +
更早内容的 LLM 摘要(保证长期一致性)
python 复制代码
class ConversationMemory:
    def load(self, thread_id):
        return self.backend.summary(thread_id), self.backend.history(thread_id, window)

    def save_turn(self, thread_id, user_msg, assistant_msg):
        self.backend.append(...)
        self._maybe_summarize(thread_id)     # 超过 16 条才触发

    def _maybe_summarize(self, thread_id):
        delta = "\n".join(f"{m['role']}: {m['content'][:300]}" for m in history)
        new_summary = llm.chat([{"role":"user","content":SUMMARY_PROMPT.format(old=old, delta=delta)}])

摘要 Prompt 明确要求保留三件事:

复制代码
1) 用户关注的对象(项目名/时间范围/指标)
2) 已给出的结论
3) 待办或下一步

压缩后只保留最后一轮,避免旧消息重复计入。

三种后端

后端 适用 说明
memory Demo / 单测 进程内
sqlite 单机部署 落盘,重启不丢
redis 在线服务 带 TTL(默认 7 天)

追问:为什么不用向量记忆?

向量记忆适合"检索很久以前的事实",但对"上一步我说了什么 "这种时序指代效果差, 且每轮都要额外做 embedding。 生产上常见做法是两者结合:摘要 + 窗口做主线,向量记忆做事实补充。

二、可观测:自建 Tracer

为什么不用 LangSmith

企业内网通常不允许把 prompt 出网。 所以自建了一套,同时保留适配位(设置环境变量即可并行上报)。

数据模型

python 复制代码
@dataclass
class Span:
    trace_id, span_id, name, parent_id
    start_ms, end_ms, status
    attributes: Dict   # 节点自定义属性:model、tokens、sql、rows...

@dataclass
class Trace:
    id, query, created_at, meta
    spans: List[Span]
    tokens: {"prompt": n, "completion": n, "total": n}
    llm_calls: int

用起来就是一个上下文管理器

python 复制代码
with tracer.span(trace_id, "node.plan"):
    ...
with tracer.span(trace_id, "text2sql", attempt=attempt) as sp:
    sp.attributes["raw_sql"] = raw_sql[:300]

落盘 + 实时推送

  • 落盘./.cache/traces/{trace_id}.json,可离线分析、可回放
  • 实时tracer.emit(event) → 事件推给 SSE → 前端"执行链路时间线"
python 复制代码
def add_listener(self, fn): self._listeners.append(fn)
def emit(self, event):
    for fn in list(self._listeners):
        try: fn(event)
        except Exception: pass      # 监听器异常不能影响主流程

排查问题时的实际用法

bash 复制代码
答错了 → 打开 /api/traces/{id}
      → 看哪个 span 耗时异常
      → 看 plan 拆得对不对
      → 看检索命中了哪些 chunk(分数多少)
      → 看工具返回了什么
      → 看 reflect 判断"够了"是否合理
      → 看最终 prompt 上下文

没有这套东西,Agent 出问题时只能靠猜。

三、Token 与成本控制

统计

python 复制代码
tracer.record_llm(trace_id, prompt_tokens, completion_tokens, model=resp.model)

前端右上角实时显示累计 token。

六个降本手段(项目里都实现了)

手段 做法
① 检索只喂 top-5 不把整篇文档塞进去
② 记忆压缩 窗口 + 摘要,不塞全量历史
③ 重排默认零调用 heuristic;必要时才上 LLM rerank
④ 迭代上限 防死循环烧钱(最重要!)
⑤ 规划用小模型 Prompt 里已预留模型切换位(LLM_FALLBACK_MODELS
⑥ 流式输出 用户可提前中断,减少无效生成

一次真实调用的开销

ini 复制代码
问:本月各项目成本排名前 3 是多少?
prompt=3000  completion=509  total=3509
(含 2 次规划 + 2 次 SQL 生成 + 1 次反思 + 1 次回答)

这个数字本身也是面试素材:你能说清一次问答到底花多少 token, 比空谈"我们做了成本优化"有说服力得多。

四、三者的关系

arduino 复制代码
记忆 ──▶ 决定"喂给模型什么"      ──▶ 影响成本
可观测 ─▶ 记录"花了多少、哪里慢"  ──▶ 指导优化
护栏 ──▶ 决定"能不能执行"        ──▶ 控制风险

这三件事 + 前面的 RAG 和编排,才构成一套能上生产的 Agent 系统, 而不是一个"接了 API 的聊天 Demo"。


下一篇:08 · 前端:Vue3 + SSE 流式与执行链路可视化

相关推荐
再吃一根胡萝卜1 小时前
04 · RAG 全流程:从切分到引用溯源
面试
再吃一根胡萝卜1 小时前
11 · 如何把项目写进简历与面试
面试
再吃一根胡萝卜7 小时前
08 · 前端:Vue3 + SSE 流式与执行链路可视化
面试
再吃一根胡萝卜7 小时前
03 · 后端:FastAPI 与分层架构
面试
Interview Aid11212 小时前
Walmart Global Tech SDE 三轮面经|基础、并发、压力面
面试·职场和发展
ocean210316 小时前
2025-2026年AI提效与实践大厂面试高频问题
人工智能·面试·职场和发展·提示词工程·ai提效
程序员梅雨17 小时前
Linux & Shell 实用干货
linux·运维·服务器·后端·面试·php
YonyouHRSaaS19 小时前
AI视频面试系统定义、功能作用、品牌推荐、选择攻略
人工智能·面试·职场和发展·ai面试·视频面试·ai视频面试
小鱼爱吃草灬灬20 小时前
实时面试辅助排查清单:音频来源、问题输入与上下文
面试·职场和发展·音视频