整体架构
CongoRAG 是模块化单体,不是微服务。交付形态是两个进程:
apps/api:HTTP 服务,提供 REST、SSE 和 go:embed 进来的前端。apps/worker:River 的消费端,处理文档和孤儿文件对账。
两个进程共用一个 PostgreSQL。业务表、向量(pgvector)、队列表都在这个库里,所以“写业务记录”和“入队”可以放在一个事务里。
internal/ 下有八个包:
domain、platform、llm、knowledge、retrieval、ctxmgr、conversation、agent。
依赖方向有几条约定:
- 业务包不 import gin,也不知道 HTTP 状态码。
- 跨包的“我需要什么”由消费方声明成 port,实现方零 import 边。
- 两个进程各有自己的装配根,重复十几行是正常的,不造共享装配包。
这些约定写在代码注释和 ADR 里。改代码时不要绕过去,否则编译期能过的边界会慢慢消失。
contracts/openapi.yaml 是唯一真相。Go 的接口和 TS 的类型都从它生成:
- Go:oapi-codegen,产物
apps/api/internal/api/generated.go - TS:openapi-typescript,产物
packages/api-client/src/schema.d.ts
契约的 info.version 跟产品版本走,当前是 5.0.2。兼容边界是路径里的 /api/v1,不是 info.version。
写入侧是异步的:上传的文档先落盘,再入队,由 worker 解析、切分、向量化、写索引。
读取侧是同步的:提问后检索、组装上下文、调用模型,再通过 SSE 下发。
两条链路共用同一个数据库,但不在同一个事务里。检索失败不会导致消息写不进去;文档处理失败也不会把已经写好的会话弄坏。
技术取舍写在仓库的 ADR 里:
- ADR-001 模块化单体
- ADR-002 契约版本
- ADR-003 两套迁移系统
- ADR-004 halfvec
- ADR-005 SSE over fetch
- ADR-006 checkpoint 分层
- ADR-007 恢复语义
- ADR-008 幂等重放窗口
- 不是微服务:没有服务间 RPC,没有消息中间件。
- 不是无状态服务:文档在磁盘,向量和业务数据在 PostgreSQL。
- 不是多租户:一个实例一组数据,没有账号和权限层。