携程员工:在互联网大厂里面算是比较 WLB 的,还可以爽用 Cursor 等。十点多上班,七八点下班,每周一天居家(附Agent面试题)
刷到一位老哥分享自己的工作体感,说在互联网大厂里携程算是比较讲究工作生活平衡(WLB)的,平时十点多到岗,晚上七八点下班,每周还有一天居家办公,日常写代码还可以痛快使用 Cursor 这类 AI 编程工具。

我一直觉得,稳定且有余地的工作节奏,才是最好的成长土壤。
我也帮大家去调研了一下携程目前的 AI Agent 应用场景,旅客端最值得关注的是 TripGenie,已经开始承接真实交易任务。

TripGenie 是一个 AI 旅行助手,能力包括复杂行程规划、实时信息检索、语音与图片交互、菜单识别翻译,以及旅行中的实时讲解。
比较明确的 Agent 能力是 eSIM 和景点门票的代理预订,用户在对话中表达需求、比较和选择产品,再确认支付。
底层依托携程自研的旅游垂类大模型携程问道,并已接入如 DeepSeek 等模型能力。
就目前的阶段来看,靠近 Agent 我觉得就是目前的版本答案。

所以,来继续学起来吧,坚持下去,日积月累,你就能感受到身体、精神上的质变。
content
01、Agent如何组合多个原子能力?
“在 Agent 里,单个内置工具或外部 MCP 接口只是一项孤立的原子能力,比如读文件、搜代码、执行命令或查询航班。要把它们组合成解决复杂问题的动作序列,需要从读写分流、Skill 入手。”

“为了兼顾速度与安全,我们在工具执行层对所有工具做了读写分级。只有文件读取、目录列举、代码检索、网页搜索和抓取这类只读工具被列入并发白名单。
当模型返回一批工具调用时,调度器会把连续的只读工具调用归类成批处理;一旦遇到文件写入、编辑、命令执行、MCP 调用或者快照回滚等工具,必须先等前面的只读批次全部返回,再按顺序逐个串行,最后按原始顺序拼回上下文。”
“跨多轮对话时,我们引入了渐进式加载的 Skill 体系,把领域专家的工具组合经验写成结构化的操作手册。
会话启动时,只注入 Skill 名称和简短描述作为索引;当模型判断任务需要某套 Skill 时,主动调用 Skill 加载工具,在同一轮工具结果后追加一条带内部标记的 user 消息,把完整的操作流程按需注入上下文,指导模型串联多个工具。”

“如果任务规模进一步扩大,涉及跨模块的大量原子能力调用,PaiCLI 会切换到先规划后执行的 Plan-and-Execute 模式或 Multi-Agent 团队模式。
规划器先把目标拆解成带依赖关系的 JSON 任务图,执行前由 Java 代码校验节点的唯一性与依赖关系,并用深度优先搜索做 DAG 环检测。
校验通过后,编排器按拓扑排序把无依赖的子任务分批并行推进,只有声明了依赖的下游节点才能继承上游输出;一旦子任务失败且整体进度未过半,编排器会携带已完成的证据和失败原因触发重规划,进度过半则将下游标记为跳过。
在团队模式下,系统把规划器、执行 Worker 和审查器拆开,规划器和审查器的请求里剥离工具定义,只有执行 Worker 能调用的原子工具,每个并行步骤配独立的审查器实例,不合格就打回重做。”
这个组合场景的使用规模和价值有多大?
“生产环境里几乎没有哪个真实需求能靠单次的原子工具调用完成。比如‘给订单模块新增带权限过滤的分页查询接口’,Agent 在一次任务里往往要经历多轮推理、连续调用十几次原子工具,先检索定位代码,再并发读取实体类与已有接口,加载代码规范 Skill 后修改代码、触发语法树静态诊断,最后运行单元测试并根据报错微调。
在我们自建的 Native AgentBench 评测体系里,考察多工具组合编排和长程多轮对抗的题目权重占到了大半。”

“多原子能力组合的工程价值,在于把大模型的‘文本输出’升级成了经得起外部环境验证的‘确定性交付’。
以前只调用单次模型或单一检索工具时,模型给出的代码片段,还得靠肉眼核对和运行测试;把检索、读取、编辑、语法检查和命令执行组合成受控流程后,Agent 可以在交付前自己发现语法错误、运行单元测试。
放到携程这类在线旅游业务里也是一样,面对跨城市的行程规划、机酒预订需求,Agent 要把口碑检索、余票查询、房屋比价、退改核验组合起来,才能把用户在多个页面间来回比对的决策过程缩短,转化为最终支付。”
02、当前Agent如何保留短期对话上下文?
“在 PaiCLI 里,短期对话没有额外设计内存缓存副本,直接就在当前 Agent 的内存里维护,然后把每次请求原样发送给大模型。”

“不过我们在消息结构上做了三处处理。每次大模型发起工具调用时,返回的 assistant 消息会携带一组工具调用编号,工具执行层无论并行还是串行,都会按原始顺序生成带相同编号的 tool 结果消息追加到列表末尾,确保严格配对。
如果接入的是带深度思考能力的大模型,模型调用工具时的思考过程也会保存在这条 assistant 消息里,并在同一回合的后续工具调用中原样带回,防止多步推理中工具调用错乱。
对于上传截图的多模态会话,当前回合内的图片随消息一旦发送,系统会自动剥离原始图片,只保留文本描述,避免截图反复消耗 Token。”
用户中途取消或进程重启时怎么保证短期上下文不损坏?

“先看中途取消。如果模型刚输出一条包含多个工具调用的 assistant 消息,第一个工具刚执行完用户就按下了取消键,这个时候如果直接中断,消息列表里就会留下没有对应结果的孤立工具调用,下一轮请求发往大模型时会触发协议报错。
为了把取消也做成有序撤退,我们在每轮迭代开始前、模型响应返回后以及每个工具执行前设了三个取消检查点。
一旦在工具执行阶段收到取消信号,系统强制终止正在运行的子进程树,并为每个未完成的工具调用自动生成一条‘用户取消了此次工具调用’的 tool 结果追加进历史消息,保证任何时刻停下来的短期上下文都是合法的。”
“再看跨进程的会话恢复。我们在本地磁盘维护了一份只追加写的会话日志(Append-only Log,类似数据库的 WAL)。
会话运行期间,每条新增的 user 消息、assistant 消息和 tool 结果都以 JSONL 格式顺序写进这份会话日志。
只要内存里的消息列表发生了改变(如清理旧工具结果、生成历史摘要或执行回滚),Agent 就立刻往日志末尾追加一条视图检查点(Checkpoint)事件,把修改后的当前消息视图记录下来。
当用户输入恢复命令接续会话时,恢复器只需找到最后一条 Checkpoint 并往后重放增量消息,就能还原中断前的短期上下文。
在团队模式下,每个执行 Worker 各自持有独立的短期消息列表,单步任务结束会立即清空,只向下游传递所需的摘要,避免上下文污染。”
03、超出最近几轮的历史上下文如何处理?
“如果按固定数量做滑动窗口截断,把早期对话直接删掉,Agent 很快就会忘掉用户在开局交代的约束条件;反过来,如果一上来就调用大模型对历史消息做全文总结,不仅额外增加延迟和 Token 开销,总结过程本身也是有损的,很容易丢失早期的文件路径、报错信息和代码细节。
所以我们对历史上下文设计了三档压缩策略,同时把关键约束提取到长期记忆里。”

“在历史消息还没有堆积之前,系统先把单条体积过大的工具输出进行处理。Agent 执行全局搜索时,只要单条输出超过字符上限,系统就会把完整内容写入本地的独立文件,只保留文件路径、分段读取建议以及首尾的预览片段,避免单次大输出把窗口占满。”
“随着对话的继续进行,之前的工具输出继续留在上下文里已经没有意义了。这时候系统会启动针对旧工具结果的清理工作,保留最近几条工具结果不动,扫描更早的历史消息,将较长的旧工具结果正文统一替换成简短的占位符,但严格保留 role 类型和工具调用编号。
这种方式不需要调用大模型,零延迟、零 Token 消耗就能腾出大片空间。”
“如果清理完旧工具结果后,早期的内容依然占用过多空间,系统才会启用结构化摘要。为了不影响当前正在处理的任务,系统会保留最近几轮完整对话不参与压缩,切分点必须落在 user 消息的边界上,保证工具调用和结果不被切断。
历史消息会先生成分段摘要,再合并成结构化历史摘要。早期历史里的 Skill 正文不会被放进摘要里,系统会直接将它们移出,并在摘要末尾列出 Skill 名称,提醒模型后续按需重新加载。”
“除了在当前会话内做压缩,早期对话里往往还包含一些后续要用到的项目规范,比如 JDK 编译版本或接口返回格式。
每轮对话结束时,系统会从用户的输入里把这类长期有效的约束提取出来,过滤掉敏感词和临时指令,经过冲突检测后存入本地的长期记忆库。这样就算早期对话已经被压缩成摘要,这些核心约束依然会通过系统提示词稳定传给模型。”
04、什么时候触发上下文压缩?压缩窗口使用什么指标和阈值?
“触发时机分为每次调用大模型前的自动检查和用户的手动触发两类。
每当 Agent 准备发起大模型请求前,都会先评估当前上下文的体积,按条件依次触发大输出落盘、旧工具结果清理和完整历史摘要。此外,当用户在终端输入压缩命令时,系统也会立即触发一次完整的结构化摘要,只保留最近一轮对话,把其余历史全部压缩。”

“在压缩窗口的评估指标上,我们同时使用‘单条消息字符长度’和‘整个上下文窗口的估算 Token 数’两项指标。所有 Token 阈值都根据当前模型的上下文窗口大小动态计算,用户在会话中途切换模型时,阈值会自动重新计算。”
“具体到三档压缩的阈值设计,第一档看的是单条字符长度,工具返回结果一旦超出上限,就立即写入本地文件并替换为首尾预览。
第二档的旧工具结果清理和第三档的完整历史摘要,看的都是整个上下文的估算 Token。系统先根据当前模型的总窗口大小算出完整摘要的触发条件,做法是从总窗口里扣除两块预留空间,一块留给模型接下来的思考与输出,另一块留给本轮工具的执行结果和语法诊断。
算出摘要触发条件后,第二档的触发线就定在摘要触发线的六成左右。达到触发条件时,先执行旧工具结果清理;等 Token 继续上涨触及到摘要触发条件时,才调用大模型生成完整的历史摘要。”
压缩后的摘要如果漏掉关键约束怎么校验和兜底?
“调用大模型生成摘要很容易遇到两个问题,生成的摘要过于宽泛,遗漏了用户提出的技术约束和尚未解决的报错信息;或者在对话轮次还不算多的时候,生成的摘要加上保留轮次,总 Token 数反而比压缩前还多。”

“为了保证摘要质量,我们做了两道校验。
提示词要求大模型生成的摘要必须包含四个固定部分,依次是‘当前目标与成功条件’、‘用户要求与已确认的决定’、‘已完成的工作及证据’以及‘未解决的问题与下一步’。摘要返回后,代码会按顺序检查这四个部分是否齐全。
如果缺少任何一个部分,系统会发一次修复请求让模型补齐;如果修复后依然缺失,或者压缩后的估算 Token 数没有小于压缩前,就立即判定本次压缩无效,直接丢弃这份摘要,保留原始对话历史。
只有当四个部分全部校验通过且 Token 数下降时,系统才会用‘结构化摘要加最近保留轮次’替换内存里的消息列表,并往会话日志里写入 Checkpoint。”
05、Agent的写操作权限通常如何控制?
“一旦 Agent 拥有了修改本地源码、执行终端脚本和调用外部 MCP 服务的写操作能力,权限控制就绝不能只靠系统提示词里的一句‘请谨慎操作’,因为提示词随时可能被外部网页或代码仓库里的提示词注入绕过。
在 PaiCLI 里,我们对所有的写操作设计了按顺序执行的四道防线,依次是当轮来源校验、人机交互审批、参数与路径校验,以及操作系统内核沙箱,还配有脱敏审计日志和快照回滚。只要前一道防线拒绝了操作,哪怕用户手动点同意也无法放行。”

“当大模型发起写操作或外部调用时,先经过来源校验。这份授权白名单在每轮开始时只根据用户输入的原始文本生成,展开后的引用文件和 MCP 资源一律不参与授权。
如果模型试图调用网络下载、网页抓取或浏览器导航,系统会强制核对目标网址。只有用户输入里明确写有网址才允许访问;模型从网页正文里看到的链接或自己拼接的网址一律拦截,切断通过恶意链接把本地代码外发的路径。
同样,只要当前会话读取过外部网页或 MCP 资源,系统就会立即锁死长期记忆的写入权限。除非用户在本轮输入里明确要求‘记住’,否则禁止保存长期记忆,防止外部不可信的内容污染跨会话记忆。”
“通过来源校验后,请求进入人机交互审批,而在执行前还要经过参数校验、路径检查和系统沙箱。
我们用 JSON Schema 在执行前先做参数检查,参数缺失或类型不对直接报错。针对文件写入和编辑,系统先把目标路径规范化,再顺着目录树向上解析真实的物理路径以消除软链接跳转,严格校验真实路径是否在项目根目录内,并对工作区内的 Git Hook 和环境配置文件做写保护,防止目录穿越与软链逃逸。
针对终端命令执行,除了高危命令的黑名单外,开启系统沙箱时,macOS 下通过 Seatbelt、Linux 下通过 bubblewrap 在内核层把系统目录设为只读、隐藏用户主目录、只允许写入当前工作区并关闭外部网络,同时在启动子进程前剥离含密钥和口令的敏感环境变量。”
“同时,我们在每轮对话开始前和结束后都会调用 JGit,把当前工作区状态提交到用户主目录下的独立 Git 快照里,排除构建产物与依赖目录,不触碰项目本身的 Git 提交历史。一旦某次写操作改坏了代码,随时可以触发回滚,把工作区文件还原到指定轮次之前的状态。”
自动模式下怎么兼顾高频工具调用体验与防注入安全?
“如果把所有写操作都设成每次必弹确认框,一个任务调用十几次工具就要频繁打断用户,用户习惯性连续回车确认后,审批机制反而失去了作用;可如果提供‘全部无条件放行’的开关,一旦遇到提示词注入就会带来安全风险。
所以我们只提供了自动(auto)、规划(plan)和询问(ask)三档模式。”

“在询问模式下,所有写操作、终端命令、回滚和 MCP 工具都会弹出确认框。而在默认启动的自动模式下,我们按操作是否可逆以及破坏范围做了分流。
因为工作区内的文件写入和编辑已经有真实路径检查兜底,并且每轮都有独立的 JGit 快照可以随时还原,所以工作区内的常规文件修改直接放行;浏览器工具里的常规页面操作,在核验过网址来源合法且属于 Agent 自建标签页后,也直接放行并记录审计日志。”
“至于破坏范围大且不可逆的终端命令执行、外部 MCP 写操作与工作区回滚,自动模式下会进入独立的审查通道。
每当模型请求执行终端命令,系统先把请求交给一个独立的审查模型。这次审查请求完全隔离了任何工具返回结果,上下文里只传入用户的原始输入、工具名和调用参数。因为审查模型看不到外部网页或文件里的不可信内容,提示词注入就无法诱导它放行恶意命令。
审查采用严格的默认拒绝(fail-closed)策略,一旦超时、报错或返回的不是约定的允许结构,一律判定不放行。不放行的操作不会弹窗打扰用户,而是直接作为失败结果退回给主模型,提醒主模型换安全的内置工具;只有当主模型在同一轮里连续多次被拦下时,才升级弹窗交由用户决定。”
ending
昨天去大学讲了一天的课。
能感受到这个世界的隔离和参差,很多学生愿意学,也愿意用 AI,但老师还在讲 OpenCV 识别车牌号这种项目。
也就导致他们并不愿意上课,但又必须上课。
而我讲的内容刚好又都是他们需要的,就互动很高频。
我很开心。
继续加油,我们下期见。
