AI 助手 01:先做一个可验证的企业级对话闭环
记录 Enterprise AI Assistant Platform 的第一轮实现:为什么先打通会话、上下文、模型调用和调用记录,而不是直接堆 RAG 与 Agent。
所属项目:AI-assistant这次迭代,我没有从 RAG、工具调用或多 Agent 开始,而是先完成一个能被验证的企业级对话闭环:创建会话、发送消息、调用模型、保存消息与调用记录,并让前端能回看结果。
它看上去像一个很基础的 Chat Demo,但边界并不一样。Demo 只要能返回一句回答;企业应用的最小闭环则需要回答三个问题:谁在调用、调用了什么、这次调用是否能追溯。这三个问题没有先解决,后面接入知识库、权限过滤和工具调用时,返工会很重。
为什么第一步不是 RAG
项目的目标是逐步实现一个企业级 AI 助手平台,后续会包含文档知识库、权限过滤、评测、成本统计和模型网关。但这些能力都依附在一次明确的模型调用之上。
如果一开始就把检索、重排和 Agent 编排塞进来,问题出现时很难区分:是前端会话没带对、Java 的业务边界有误、Python 适配层异常,还是模型服务本身不可用。
因此这轮迭代优先保证四个可观察的落点:
- 会话和消息能按租户、用户隔离并持久化;
- Java 服务把请求上下文传给 AI 服务,而不是让后者猜调用者;
- Python 服务可在本地
demo模式完成确定性验证,也可切换到 OpenAI 兼容接口; - 成功和失败的调用都留下 provider、模型、token、延迟、请求 ID 与错误信息。
当前闭环的边界
前端的 Chat Workspace 负责会话列表、消息展示和提交状态;它会为每条用户消息生成 clientMessageId。Java 后端是业务边界:读取租户、用户、角色和请求 ID,校验会话归属,写入用户消息,再调用 Python AI 服务。
Python 服务不保存业务会话,也不负责权限判定。它根据 LLM_PROVIDER 选择 demo 或 OpenAI 兼容实现,把统一格式的答案、模型信息、token 和耗时交回 Java。最后由 Java 写入助手消息和调用日志。
这层分工是有意为之。Java 更适合承接企业系统已有的身份、审计和数据约束;Python 则保留给模型 SDK、检索与编排生态。以后接 RAG 时,Python 可以扩展检索链路,但仍然通过 Java 取得已经确定的调用上下文。
一次消息是怎样完成的
当前 API 的关键入口是:
POST /api/chat/sessions/{sessionId}/messages
正常路径如下:
- 前端首次发送时创建会话,并提交消息与
clientMessageId。 - Java 按
tenant_id + user_id + session_id校验会话;跨租户或跨用户的会话不会被读取或写入。 - 若同一个
clientMessageId已经有对应的助手回复,直接返回已有结果,避免重复点击或网络重试造成二次模型调用。 - Java 保存用户消息,携带
x-request-id、租户、用户和角色头调用 Python 的/chat。 - Python 适配层调用模型,Java 保存助手消息和调用日志,再把结果回给浏览器。
这里的幂等策略只覆盖“已经完整得到助手回复”的重复请求。若用户消息已落库、模型调用却失败,当前实现会记录失败调用,但再次提交不会命中缓存,仍可能再次请求模型。这是 V1 有意保留的边界:先让失败可见,后续再根据真实重试需求设计消息状态、超时恢复和补偿任务,而不是假装一次事务能够覆盖外部模型调用。
为验证而保留的 demo 模式
模型接入默认使用 demo provider,返回确定性的 [demo] 原消息。这不是产品能力,而是开发期的验证工具:数据库、Java、Python 或前端任一层出了问题,都可以在不依赖 API Key、余额和外网稳定性的情况下定位。
当需要接真实服务时,只需在根目录 .env 设置:
LLM_PROVIDER=openai-compatible
LLM_BASE_URL=https://你的模型网关地址
LLM_API_KEY=你的密钥
LLM_MODEL=模型名
密钥不进入代码库。配置缺失或 provider 不受支持时,Python 服务应返回不可用状态,而不是悄悄降级成看似成功的空回答。
这轮没有把“流式”做成假流式
Java 已经提供 SSE 入口 POST /api/chat/sessions/{sessionId}/messages/stream,用于验证浏览器到后端的事件通道;但当前前端仍调用同步消息接口,而且 Java 的 SSE 实现是在拿到完整回答后再拆分发送。
所以它还不是真正的模型 token 流式输出。把“分段推送完整答案”写成“流式响应”会掩盖延迟、断连、取消与半段落库等真正难题。下一轮若启用真实流式,需要让 Python 透传模型流、Java 逐段转发,并明确客户端断开时的会话状态与调用日志如何收尾。
这次迭代如何验收
从项目根目录启动:
docker compose up -d --build
随后检查 Python 的存活与依赖就绪、Java 的健康检查,以及前端的会话创建和消息发送。特别要观察两类结果:一次成功调用是否同时生成用户消息、助手消息和调用日志;一次错误配置是否保留失败日志并返回明确错误。
这个闭环适合当前的 V1:它足够小,能本地运行,也为后续的 RAG、权限检索和 Agent 工具调用保留了清晰接入点。它并不适合直接承诺生产级高并发或完整流式体验;那些能力需要在真实模型接入、压测数据和故障场景明确之后再逐层补上。
下一篇会在这个基础上处理文档进入知识库后的第一个关键问题:检索命中的内容,怎样与租户、用户和文档权限一起被约束。