Octop 管会话路由、模型服务管内容生成,自托管边界要分开看

文章导读
出现「像是 Octop 在回答,但内容其实是模型服务生成的」这类现象时,先别急着调模型参数或改会话结构。Octop 侧负责的是会话路由:会话归属、消息顺序、角色标注、上下文与提示词的拼装、把请求发给哪个模型服务;模型服务侧负责内容生成:拿到请求后产出文本。两者自托管时边界容易被界面掩盖,排查的第一步是把「会话状态」和「回答文本」当成两条独立信息分别记录,再对照两侧日志,判定问题落在哪一侧。
📋 目录
  1. A 在 Octop 界面发一条消息并记录可见反馈
  2. B 查会话记录中的消息 ID 与角色
  3. C 对照模型服务端收到的请求
  4. D 用同一问题切换模型配置做对比
  5. E 把问题归到会话侧或模型侧
A A

出现「像是 Octop 在回答,但内容其实是模型服务生成的」这类现象时,先别急着调模型参数或改会话结构。Octop 侧负责的是会话路由:会话归属、消息顺序、角色标注、上下文与提示词的拼装、把请求发给哪个模型服务;模型服务侧负责内容生成:拿到请求后产出文本。两者自托管时边界容易被界面掩盖,排查的第一步是把「会话状态」和「回答文本」当成两条独立信息分别记录,再对照两侧日志,判定问题落在哪一侧。

Octop 管会话路由,模型服务管内容生成,判断时先记录界面上的会话状态与回答文本,再对照会话记录的消息 ID、角色和模型服务端收到的请求。能定位到会话侧就不要改模型参数,能定位到模型侧就不要动会话结构。两侧都自托管时,边界以实际配置和日志为准,不确定的部分用同一问题切换配置复测来确认。

在 Octop 界面发一条消息并记录可见反馈

界面上同时混着两类信息:会话状态(发送中、已送达、失败、重试、会话标识)和模型内容(回答正文)。这两种反馈的来源不同,记录时要分开写,避免后面拿错误提示去反推模型行为。

建议按下面的清单逐条记录,一次只发一条消息,减少干扰:

  • 输入原文与发送时间(尽量精确到秒,便于和模型服务日志对齐)
  • 界面状态变化:发送中、已送达、失败提示、重试入口是否出现
  • 返回文本:是完整回答,还是兜底话术、模板占位或空内容
  • 是否有加载指示、超时提示、错误码或错误文案
  • 同一会话是否出现重复消息、顺序颠倒、角色标错

验证方式:另开一个窗口或刷新会话列表,看这条消息是否即时出现在会话记录里。如果列表没更新但回答正常显示,说明会话落库和内容生成可能走了不同路径,这一点后面查消息表时要用到。

关注面会话侧(Octop)模型侧(模型服务)
会话归属与可见性决定谁能看到哪条会话、会话如何分组一般不感知会话,只接收请求
消息顺序与角色保存 user / assistant / system 的角色与顺序只看到拼装后的输入
提示词与上下文拼装通常在这一侧拼装,也可能由模型服务自带模板,需要看请求体确认可选:套用自身模板或做参数校验
实际文本生成不生成内容生成全部回答文本
超时与重试负责到模型服务的调用超时与重试策略负责自身推理超时与错误码

查会话记录中的消息 ID 与角色

打开会话详情、会话导出或后台的数据视图,确认 Octop 是否把用户消息、助手消息、系统提示分开存储。字段名以实际界面或导出结构为准,常见的是消息 ID、会话 ID、父消息 ID、角色、内容、创建时间、模型名。

下面是一段结构示意,只用于说明要核对哪些字段,字段名请以实际导出为准:

{
  "session_id": "...",
  "message_id": "...",
  "parent_message_id": "...",   // 用于判断顺序与分支
  "role": "user | assistant | system",
  "content": "...",
  "created_at": "...",
  "model": "..."                // 是否记录取决于实现
}

判断要点:如果 user 消息存在、assistant 消息的 content 为空或缺失,但界面确实显示了回答,说明回答没有回写到会话侧,需要查流式返回结束后的写入逻辑,而不是先怀疑模型。如果 system 提示在导出里能看到原文,下一节对照模型服务请求时可以直接比对这段内容是否被完整传入。

对照模型服务端收到的请求

在模型服务的访问日志或监控面板里,找这次请求对应的记录,重点看四项:请求时间、输入长度、返回状态、耗时。请求时间与界面发送时间的差值,可以判断延迟发生在哪一段;输入长度(字符数或 token 数,按实际统计口径)与界面上会话上下文的规模对比,可以判断上下文有没有完整传过去。

Octop 管会话路由、模型服务管内容生成,自托管边界要分开看

命令与路径按实际部署替换,下面只是抓取思路:

# 按时间窗口过滤最近的请求记录
 grep -n 'T10:' /var/log/model-service/access.log | tail -50

# 只打印状态码与耗时字段(字段位置以实际日志格式为准)
 awk '{print $1, $4, $9, $NF}' /var/log/model-service/access.log | tail -50

三种常见结果:一是模型服务完全没有收到请求,问题在会话侧到模型服务的调用链路,通常与地址、鉴权、超时配置有关;二是收到请求但输入长度明显短于会话上下文,说明上下文没传全,要回头查会话侧的拼装逻辑;三是收到请求且返回状态异常,此时优先看模型服务自己的错误码和资源情况。

用同一问题切换模型配置做对比

用同一条输入、同一个会话,切换模型配置后再发一次,对比两边的差异。切换时只改一个变量,别同时改模型和提示词,否则无法归因。建议记录下面几项:

记录项切换前切换后
模型名占位符(以实际配置为准)model-amodel-b
系统提示 / 模板差异记录原文或摘要记录原文或摘要
模型服务收到的输入长度记录数值记录数值
返回文本差异记录风格、长度、结构记录风格、长度、结构
返回状态与耗时记录记录

判断方向:如果换模型后回答风格、措辞、结构化程度变化明显,但会话里的上下文和消息顺序没变,内容生成主要落在模型侧;如果两种模型下都出现同一段固定前缀、同一句免责话术或同样的截断方式,这段多半来自会话侧的模板或后处理。需要结合环境确认的一点是:有些模型服务会自带默认模板,切换模型时模板也随之变化,这种情况要同时看请求体才能分清。

把问题归到会话侧或模型侧

形成一套固定顺序,可以减少来回改配置的无效动作:

  1. 先看界面有没有错误或超时提示。有提示时,先查会话侧到模型服务的调用链路。
  2. 再看会话记录里 user 与 assistant 是否成对落库。缺 assistant 记录,查回写和流式结束处理。
  3. 再看模型服务是否收到请求、输入长度是否与界面上下文匹配。不匹配,查会话侧上下文拼装。
  4. 最后用同一问题切换模型或提示词各跑一次。回答随模型变,归模型侧;随模板变,归会话侧。
  5. 确定一侧后只改这一侧,用同一输入复测,确认改动生效再继续。
会话侧症状模型侧症状
消息顺序错乱、角色标错回答语言、风格、知识范围与模型特性相关
会话列表不更新、跨会话串消息返回被截断、停止条件异常
assistant 消息缺失或重复写入状态码 4xx / 5xx、推理超时
请求没发出去、鉴权或地址错误请求已到达但输入长度偏短
固定前缀、免责话术重复出现同一输入在不同会话中输出稳定且与模板无关

按这个顺序走一遍,多数情况能判断出该查会话路由还是该查内容生成。两侧都自托管时,边界不是靠约定,而是靠日志、消息记录和可复现的对比结果来确认。