快手 AIAgent开发 一面(日常)

1. 自我介绍

2. 介绍下你简历上这个项目

3. 聊聊记忆系统模块设计,你会怎么拆

记忆系统至少要拆成写入、索引、检索、融合和淘汰五层。写入层不是把所有对话都存下来,而是先判断值不值得记;索引层要同时支持按用户、会话、任务、实体和时间切片检索;检索层通常要把结构化过滤和语义召回结合起来;融合层负责把召回结果转成当前 prompt 可消费的上下文;淘汰层解决过期、冲突和污染。好的记忆系统不是“记得多”,而是“能在正确时刻拿回正确事实”。

4. 记忆检索到底应该用 user_id、session_id,还是别的主键

不能只用一个主键。session_id 更适合短期上下文恢复,能还原当前任务过程;user_id 更适合长期偏好和跨会话事实;真正生产里通常还会加 task_idworkspace_identity_idthread_id,因为同一个用户往往同时跑多个任务。只按 user_id 检索,容易把无关历史混进当前任务;只按 session_id 检索,又拿不到跨轮长期信息。记忆检索本质上是多维过滤,不是单键查库。

def build_memory_key(user_id, session_id=None, task_id=None, entity_id=None):
    return {
        "user_id": user_id,
        "session_id": session_id,
        "task_id": task_id,
        "entity_id": entity_id
    }

print(build_memory_key("u01", session_id="s88", task_id="case_1024"))

5. 长期记忆除了本地文件系统,还应该落在哪里

本地文件系统只适合原始快照、调试回放和轻量缓存,不适合作为唯一长期记忆存储。真正长期记忆通常会分层:原文和快照进对象存储,结构化事实进关系库或 KV,语义检索索引进向量库,事件流和执行轨迹进日志系统。这样做的原因是长期记忆不是单一形态,事实、偏好、文档和动作轨迹适合不同的存储与查询方式。把所有记忆都塞到一个地方,后面很容易在检索效率、更新一致性和权限控制上出问题。

6. OpenClaw 这类设计和传统 Agent 实现最大的区别是什么

传统 Agent 很多时候更像“Prompt + Tool Call + while loop”的轻量封装,模型负责大部分控制逻辑。OpenClaw 这类设计更强调运行时,把计划、状态、工具、观察、回放和恢复都显式建模。区别不在于能不能调工具,而在于系统是否有完整的执行语义:每一步为什么发生、失败怎么补偿、中断后如何恢复、上下文如何裁剪、动作是否可重放。真正上生产时,运行时设计往往比模型本身更决定系统稳定性。

7. 如果让你设计一个 OpenClaw 式记忆系统,最关键的抽象是什么

最关键的不是“记忆类型”,而是“记忆单元”和“可恢复状态”。记忆单元至少要包含来源、时间、适用范围、置信度、冲突关系和引用方式,不然召回之后模型不知道该怎么用。可恢复状态则要求系统能在任意 step 停下来,并基于事件日志、工具返回和已提交状态重建执行现场。很多系统把记忆做成一堆字符串片段,检索能检出来,但恢复不了过程,也解释不了结果。

8. 上下文工程到底是什么,它解决的不是 token 不够,而是什么

上下文工程解决的核心问题不是“塞不下”,而是“给模型什么才最有用”。上下文窗口里同时可能存在用户输入、系统规则、任务状态、工具结果、长期记忆、历史摘要和安全约束,这些内容彼此竞争注意力。上下文工程就是在有限窗口内,让模型看到最小但充分的信息集,既能继续做事,又不会被无关历史污染。它本质上是一个信息选择和状态投影问题,不是简单的消息拼接问题。

9. 一个成熟的上下文窗口里通常应该包含哪些内容

通常至少包含系统角色和边界约束、当前用户请求、任务状态摘要、最近若干轮对话、必要的长期记忆、当前工具返回、格式与安全约束,以及少量高价值示例。真正难的是这些内容不是固定拼接,而要根据任务阶段动态选择。比如执行型任务更依赖工具结果和参数槽位,创作型任务更依赖风格约束和用户偏好,恢复型任务更依赖最近 checkpoint 和未完成动作。窗口设计必须和状态机联动。

10. 上下文管理策略的触发时机一般怎么设

比较常见的触发条件有四类:token 使用率逼近阈值、任务阶段切换、工具结果大量回填、会话轮次跨过某个拐点。只按轮数压缩往往不稳,因为一轮工具返回可能比十轮闲聊都长;只按 token 阈值压缩也不够,因为有些状态切换点哪怕 token 还够,也应该主动做状态摘要。更合理的方式是“硬阈值 + 事件触发”混合,避免压缩过晚或压缩错时机。

11. 给每条 message 增加 token 总数记录字段,这里的 token 应该怎么计算

不能用字符数近似,也不能简单按空格切词,要按真实模型 tokenizer 计算。因为中文、英文、标点、代码和特殊符号在不同 tokenizer 下的切分完全不同。工程上一般会在消息入库时就做一次 token 估算,并记录 input token、output token、累计 token 和压缩后 token。这样后续做预算、裁剪和计费时才有依据。

def rough_token_count(text: str) -> int:
    # 这里只是简化估算,真实场景必须走目标模型的 tokenizer
    chinese = sum('\u4e00' <= ch <= '\u9fff' for ch in text)
    others = len(text) - chinese
    return chinese + others // 4

msg = "请帮我总结这段合同争议焦点,并抽取付款义务。"
print(rough_token_count(msg))

12. token 阈值应该怎么设计,尤其是用户可以自配模型 api key 的场景

这时不能写死一个全局阈值,因为不同模型的上下文长度、保留安全边界和响应风格完全不同。更稳的设计是按模型配置动态计算预算:总窗口减去系统提示、工具协议、保留输出预算和安全冗余,再得到可用于历史消息的上限。用户自配模型时,还需要在元数据里维护 max_contextreserved_output_tokenstool_schema_budget 等字段,避免某些小窗口模型直接被长历史压爆。

13. 压缩上下文时,为什么很多场景下优先压缩模型输出,而不是先压缩用户输入

因为模型输出通常更冗长、重复性更高,包含大量礼貌话术、解释性过渡语和低密度内容。用户输入虽然短,但往往承载目标、约束、纠错和偏好,一旦压缩过头,很容易丢失任务关键条件。当然这不是绝对的,如果用户输入存在长段复制文档或重复追问,也要做结构化抽取。优先压缩模型输出,本质上是优先压低冗余密度更高的部分。

14. 如果连续五轮对话里只压缩过一次模型输出,剩下四轮怎么拼接才不失真

关键不是简单把摘要插

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

AI-Agent面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论

相关推荐

最喜欢秋天的火龙果很...:第一份工作一定要往大的去,工资低点没事。后面换工作会更好找,即使你去小公司,你也不可能不会换工作的。所以找大的去
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务