Skip to main content
Glama

Celmis

自托管代码智能——向你的代码库提问、审查拉取请求,并产出审计员会索要的证据

celmis-labs.github.io · 文档 · 快速开始 · 结果

Celmis 会读取你的仓库一次,并保存它们的符号图。其他一切——提问、审查、依赖审计、生成的文档——都是读取这张图的不同方式。它运行在一台机器上,由 docker compose 管理,背后是你选择的模型提供商,除了你配置的调用之外,没有任何内容离开你的网络。

在最早的传说中,Kelmis 是冶炼者——三位 Idaean Dactyls 之一,与锤子 Damnameneus 和铁砧 Acmon 并列,铁器加工被归功于他们。索引在这里做的是还原;各个界面则负责加工成果。

仅靠 diff 的工具无法带给你的东西

提出一个跨越两个仓库的问题,答案会同时引用两个仓库:

向代码提问,跨两个仓库作答

那不是一个搜索结果。网关和支付服务是独立的仓库,没有共享代码,而答案追踪了它们之间的调用链——然后主动注意到 Kafka 主题名在两个仓库中都被硬编码了,修改其中一个会静默地破坏另一个。

一个只从结构上阅读 diff 的审查者说不出这一点。它根本没有打开另一个仓库。


Related MCP server: saglitzsecure-mcp

人们用它做的七件事

你是项目经理、交付负责人或客户,想知道一组项目的状态,或者某个东西实际是如何工作的

直接问它。从任何设备、任何地方,无需预约工程师的时间,也无需开一场只产出一段话的会议 → 问问代码

新工程师遇到了一个本需要资深工程师来回答的问题

每一次这样的提问都会在资深的人正忙于手头工作时把他们从心流中拉出来。现在由代码库来回答,并附上 file:line 引用 → 问问代码

两个团队共用一个集成,但彼此都无法读取对方的仓库

加载它,授予提问权,并拒绝那些必须保持私密的路径。他们得到答案;凭据在源头就被拒绝 → 谁能看到什么

客户或审计员索要你的 SBOM

一个按钮,生成 CycloneDX,外加一个证据包,其清单让他们无需信任你即可验证 → 依赖、SBOM 与证据包

某个依赖中出现了漏洞

Fix with Claude 会把仓库、包和漏洞发现交给一个嵌入式会话。它会进行编辑,runner 会推送分支并打开 PR → 从这里修复

一个拉取请求需要审查

智能体阅读 diff——并且在图已经构建的地方,了解还有谁会调用正在被修改的内容,包括来自另一个仓库的调用 → 拉取请求审查

你自己的智能体或编辑器需要理解代码库

把它指向 /mcp/。同一索引之上有十八个工具,遵循相同的访问规则——你的代码在任何地方都不会有第二份副本 → 连接 Claude Code 与其他 MCP 客户端

前三个是代码审查工具完全做不到的,也正是为什么这是一个平台而不是一个审查器:索引一次,然后无论你站在工作的哪一侧,都可以读取这个索引。

三个数字

197 秒

从 git clone 到六个服务健康,在干净服务器上实测

$0.118

在随产品附带的模型上,每个被审查的拉取请求的成本

50 个中的第 17 名

在 Martian Code Review Bench 离线集上,由三位评审方共同评判

最后一个数字是故意不好看的,而且它会一直保留。它衡量的是下面的一个界面——在孤立的单仓库 PR 上的拉取请求审查——而这个测试集没有兄弟服务让符号拥有消费者,因此这个产品为之而生的东西根本不在数字里。完整表格、对它判为误报的每一条发现的审计,以及可重现两者的命令,都在结果中。

目录

快速开始

你需要什么

Docker

24+,带 Compose v2

macOS/Windows 上用 Docker Desktop,Linux 上用原生引擎

模型 API 密钥

任选其一

Google Gemini、Anthropic、OpenAI、OpenRouter、Groq 或 Mistral。一个免费的 Gemini 密钥就足以评估: https://aistudio.google.com/app/apikey

RAM

约 4 GB 可用

在真实索引运行中测得:五个容器合计峰值 1.1 GB,空闲时 565 MB

Postgres 和 Qdrant 是内置的——无需准备外部集群。使用 Docker 流程不需要安装 Python 或 Node.js。

启动

git clone <your-fork-url> celmis
cd celmis

# Generates .env and fills every secret in the format each one needs.
# Idempotent: run it again after a pull and it fills only the new blanks.
./scripts/init-env.sh

docker compose --env-file .env up -d

# Wait for healthy — first boot pulls three images and applies migrations
docker compose ps

打开 http://localhost。

这里不会构建任何东西。三个镜像会从 CELMIS_REGISTRY 指定的 registry 拉取,标签由 CELMIS_TAG 指定,支持 linux/amd64 和 linux/arm64——Apple Silicon 和 ARM 服务器都能获得原生镜像。在运行它们的机器上构建,仅 api 就实测需要 485 秒和 4.2GB 磁盘,这就是为什么安装不再意味着编译。

端口是 80,不是 3000:反向代理将应用及其 API 放在同一个源上,并在 /backend 下提供 API 服务。这不是部署偏好——浏览器包请求的是相对路径,这是让一个发布出来的镜像能服务于所有安装、而不仅仅是构建它的那一个的唯一方式。

要在 Celmis 上开发而不是运行它,加上 dev overlay 就能恢复本地构建:

docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

init-env.sh --check 会报告哪些内容仍为空,而不写入任何东西。

首次安装:克隆、生成 .env、启动整个栈

这是捕获会话的渲染图,不是屏幕录制——图中的数字是 2026 年 8 月 26 日那次运行产生的,compose 输出逐字来自安装报告中的 logs/03-up.log。用绘制而不是截图,是因为无法在已运行的栈旁边再启动一个栈:docker-compose.yml 固定了 container_name,所以名称会冲突。

停止

docker compose down       # stop, keep your data
docker compose down -v    # stop and DELETE every volume

首个用户与管理员

/login 上的注册表单在栈健康后即可使用。该账户是普通用户——注册不会授予管理员权限,即使是第一个注册的人也不例外。

全局管理员则来自环境变量:使用 CELMIS_MASTER_EMAIL 和 CELMIS_MASTER_KEY(作为密码)登录,两者都在 .env 中。谁运行这台机器谁就是管理员,这是自托管安装想要的模式,而不是先到达表单的人。只有两个变量都设置后该路径才存在,而且每次使用都会写入审计日志。

要将普通账户升级为管理员:

docker compose exec api analyzer auth make-admin you@example.com

连接仓库

  1. 设置 → LLM 设置 — 粘贴一个提供商密钥。它在接触数据库之前会用 CREDENTIAL_MASTER_KEY 加密,UI 之后只会再次显示前四个和后四个字符。

  2. 连接 — 添加 GitHub、GitLab 或 Bitbucket 令牌。请使用机器账户,而不是你自己的账户:个人令牌能访问你可见的每个仓库,而且令牌最终会出现在备份、日志和截图中。

  3. 仓库 → 添加 — 从提供商选择仓库,或粘贴克隆 URL。索引会排队;任务会显示在同一页面上。

索引会从同一个检出中构建两样东西:符号图(定义、调用、导入——审查智能体所依据的内容)和 Qdrant 中的嵌入向量(问答所检索的内容)。一个 12 万符号的仓库在四核上大约需要一分钟。

二十三种语言会被解析进图中。对于没有解析器的语言,系统会明确说明,而不是静默跳过——analyzer graph-stats 会列出哪些被读取、哪些没有。


问问代码

在聊天中提问,它会从你指向的任意多个仓库中以 file:line 引用作答。答案会边生成边流式输出。

把仓库分组到一个项目中,问题就会对整组提出:

一个包含多个仓库的项目

回答引用真实代码,而且只引用提问者有权看到的代码——这正是可以放心把问题交给仓库所属团队之外的人的原因。参见谁能看到什么。

拉取请求审查

代理读取 diff,并把发现发布到 GitHub、GitLab 或 Bitbucket。与其用这个界面的截图来展示,不如把评审留在它们发布的地方——真实项目上的五十个拉取请求,评论仍然附着在它们所评论的那几行代码上。它们列在测试仓库下,那里的输出未经编辑,包括下面审计标记为错误的发现。

在构建了图谱的情况下,评审还会带上 diff 无法显示的信息:还有谁会调用正在被修改的符号,包括来自其他仓库的调用。在没有构建图谱的情况下,评审仍然会运行——只是回答更窄的问题,而基准测试衡量的正是这个问题。

基准测试判定为误报的每一条发现,都在源仓库中打开,并附上判定发布。七十九条中有三十三条最终被证明是金标准集未包含的真实缺陷。这项工作在误报审计中,每一条都附有代码和永久链接,因此你可以对其中任何一条提出异议。

依赖、SBOM 与证据包

依赖审计是确定性的:安装了工具的地方使用原生审计器,其余地方使用 OSV,不涉及任何模型。如果你给它密钥,语言模型会撰写摘要——但它不决定什么东西有漏洞。

合规工件:SBOM、证据包、技术文档

每次审计都会产出两个文件,两者都不需要 LLM 密钥:

  • SBOM —— 一份 CycloneDX 清单,列出每个依赖、其版本、包 URL 以及已知针对它的漏洞。人们说“把你们的 SBOM 发给我们”时,指的就是这个文件。

  • 证据包 —— 把审计作为一份申报材料:每个 SBOM、每条发现、过往运行的时间线,以及每个文件的 sha256,这样第三方可以检查之后没有任何内容被改动,而无需信任我们。一个内容之后可以被更改的文件夹说明不了什么;清单才是让它成为证据的东西。

与它们一起的还有生成的技术文档——根据代码编写的模块 PRD、功能文档和集成指南——这些文档归你所有,并且在任何订阅结束后仍然有效。

为什么现在需要它。 从 2026 年 9 月 11 日起,EU Cyber Resilience Act 要求制造商在 24 小时内向 ENISA 报告已被积极利用的漏洞。正式的 SBOM 强制要求要到 2027 年 12 月才落地,但如果没有组件级的可见性,你首先无法回答 24 小时这个问题——要报告哪些组件受影响,你必须先知道里面有什么。

Celmis 不声称合规,也不会这样做。 它只产出申报所需的工件。申报是否充分是律师的判断;任何暗示自己可以判断的工具,都是在贩卖虚假的安全感。

还有一件事,审计页面会直说出来,因为这是一项没有人会去找的失败:一个无人扫描的生态系统报告零漏洞,和干净的生态系统一模一样。 覆盖率会显示在发现旁边——每条结果由哪个审计器生成,以及更有用的:哪些内容未被检查,以及为什么。

从这里修复

发现问题只是闭环的一半。一个内嵌的 Claude Code 会话在安装内部运行,编辑检出目录,然后 runner 提交、推送分支并打开拉取请求。

依赖审计中的漏洞会带有一个 Fix with Claude 按钮。它不会打开一个空聊天——而是把仓库、包、两个版本以及任务边界预先写好,交给会话:

会话,根据依赖发现预先填好

下面就是这样一个完整的闭环,针对一条真实发现——lodash 4.17.11,它有一个已知漏洞。从 Start session 到打开一个拉取请求,220 秒,五轮对话:

Read package.json
  → "Only package.json has lodash; no requirements.txt/pyproject/go.mod exist here."
Edit package.json: "lodash": "4.17.11" → "4.18.0"
mcp__exec__run: cat package.json | grep -A2 lodash; ls
  → "Confirmed no other manifest files exist, so no other changes were needed."

它推送的分支,以及它在 GitHub 上打开的拉取请求:

代理打开的拉取请求,只改动了一行

看看这个 diff 里没有什么。axios 0.21.1、minimist 1.2.0、node-fetch 2.6.0 就位于紧邻的上下行——全都过时,全都在同一次审计中被标出——但全部未被触碰。任务只说了清单文件,而一个路过时顺手把另外三个也整理掉的代理,只会让评审结果更糟,而不是更好。

这是一个真实存在的拉取请求,不是截图:celmis-demo-gateway#6——分支 celmis-agent/b8960e01,一个提交,+1/-1。

已完成的会话,包含其分支和指向拉取请求的链接

那段会话记录中有两个细节比 diff 更值得注意。代理没有假定没有其他清单文件——它在沙箱中运行了一条命令来确认。任务也说了“只改清单文件,不要碰无关的依赖”,所以改动正好是一行。

runner 允许什么,不允许什么

这是由 runner 决定的,而不是由提示词决定的——这是你在授予代理任何权限之前值得先读的部分:

  • 没有自己的 shell。 禁止使用 Bash、WebFetch、WebSearch 和笔记本编辑。命令通过沙箱容器运行;沙箱容器是一个独立服务,拥有自己的 uid 和只读的根文件系统。

  • Git 是 runner 的职责。 代理从不提交或推送。当工作完成——或者当你按下 Finish & push——runner 会创建提交、推送分支并打开 PR。永远不会推到默认分支。

  • 服务商限额只是暂停,不是损失。 上面那次运行的第一次尝试在会话中途撞上了每周账户限额。会话没有终止:它转入 paused 状态,工作可在十四天内恢复,并显示服务商自己的消息,而不是一个通用的失败提示。第二个密钥完成了它。

  • 会话可观看。 输出通过 SSE 流式传输并支持回放,因此重新连接时会从上次中断的地方继续,而不是从空白开始。

连接使用的是 setup token,按用户或按工作区持有。API 一旦保存便永远不会再返回它——只会返回它是否存在,以及它是否仍然有效。

谁可以看到什么

访问权限按仓库、按团队解析,并同时作用于所有入口——Q&A、图谱、搜索、MCP:

setting

effect

visibility: none

该仓库对研究而言不存在

visibility: metadata

仅文档和架构说明

visibility: code

源代码可读

deny_globs

即使在 code 级别也优先——凭据、加密材料、数据库连接、密钥校验

allow_globs

设置后即为允许列表;deny 仍会从其中减去

这就是让相邻团队场景能够落地而不是一句承诺的原因:加载仓库,授予另一个团队提问的权限,并拒绝那些不得读取的路径。他们能得到答案;那些文件在源头就被拒绝,而不是从一个已经包含它们的响应中被过滤掉。

语言与格式

十七个图谱模块,外加一条通过 tree-sitter 标签查询实现的通用路径,用于没有专属模块的语言:

代码——Python、TypeScript、JavaScript、Go、Java、C#、C++、PHP、Vue,以及更多通过通用路径支持的语言。

基础设施——Dockerfile、docker-compose、Helm、Kubernetes 清单、Terraform 和 CI 工作流。这是大多数代码智能工具会跳过的部分,也正是问题可以从函数跨越到运行它的服务定义的原因。

确定性检查——无模型,无误报

下面每一项检查都只通过读取文件来判定。没有任何语言模型参与判断某件事是否出错,因此误报率为零是构造上保证的,而不是调参调出来的。

这个区别正是关键所在。误报率在百分之二十左右时,开发者就会完全不再读工具的评论——一条只耗费几秒注意力,一千条则让你失去一个已经学会跳过工具所说一切的团队。这里的模型只用来解释和排定优先级,绝不用于检测。

Check

Reads

Catches

install_script

package.json 生命周期钩子

在安装时运行代码的依赖

python_build_hooks

pyproject.toml / setup.py

Python 包中的构建期代码执行

cargo_build_script

Cargo.toml

带有 build.rs 的 crate

non_registry

清单文件和锁文件

从 git URL 或 tarball 拉取而非从注册表拉取的依赖

suspect_name

依赖列表

拼写仿冒(typosquats)——名称与流行包只差一次编辑

lock_drift

清单 vs 锁文件

不再与清单声明匹配的锁文件

cross_repo_drift

PR diff,然后是兄弟仓库

在一个仓库中更改、却在其他仓库中遗留的常量

普通的 CVE 扫描被有意不列入该列表。OSV-Scanner 已经在做这件事,它免费,而且是事实标准——Celmis 运行它(外加每个生态自己的审计器:pip-audit、npm audit、govulncheck、cargo audit),并把结果当作输入,而不是一项功能。

关于合规。 Celmis 产出审计所要求的工件——CycloneDX SBOM、依赖清单、带时间戳的发现历史,以及每条发现所依据的证据。它不声称你的申报是充分的,任何工具都无法诚实地这样声称:审计员接受什么,取决于你的行业、司法管辖区和你自己的控制措施。产出工件;让以此为业的人去评估它们。


连接 Claude Code 及其他 MCP 客户端

Celmis 通过 MCP 暴露其索引,因此代理可以搜索符号、读取 API 表面并找到调用方,而不必在它并不拥有的检出目录里 grep。

通过 HTTP(运行中的服务栈在 /mcp/ 提供):

# Mint a token (or issue one from Settings → MCP in the UI)
docker compose exec api analyzer mcp issue-token \
  --scopes "read:graph read:groups" --duration 86400
// ~/.claude.json  (or .mcp.json in a project)
{
  "mcpServers": {
    "celmis": {
      "type": "http",
      "url": "http://localhost:8000/mcp/",
      "headers": { "Authorization": "Bearer <the token you just minted>" }
    }
  }
}

通过 stdio,无需经过 HTTP:

{
  "mcpServers": {
    "celmis": {
      "command": "docker",
      "args": ["compose", "exec", "-T", "api", "analyzer", "mcp", "serve"]
    }
  }
}

代理可以问什么

HTTP 接口提供 18 个工具。它们能回答 grep 无法回答的问题:

list_workspace_repos

哪些仓库存在、已建立索引、有文档、自动评审已开启

search_symbols

函数或端点在何处定义,跨整个项目

find_consumers

哪些仓库调用某个符号——包括你从未克隆过的仓库

get_api_surface

服务实际暴露的 HTTP 处理器

get_owner · list_deprecations

谁拥有某个文件;什么在被淘汰、谁还在使用它

route_incident

给定一个堆栈跟踪,它属于哪个仓库和哪个负责人

bootstrap_client · start_integration_walk

客户端调用另一个团队的服务需要什么

get_dep_audit · list_dep_findings

上一次审计及其发现,最严重的排在前面

get_review · get_review_policy

某个 PR 的最新评审,以及哪些代理在何处运行

这两种传输方式提供的工具并不是同一套。 通过 stdio 提供的 analyzer mcp serve 提供 13 个较老的图状工具(find_symbol、find_callers、query_graph);HTTP 挂载点提供的是上面这 18 个。两者互不为对方的子集——要哪套工具,就选对应的传输方式。

一份分步指南,包含每个工具所需的权限范围和故障模式,见 .claude/skills/celmis-mcp/SKILL.md。当此仓库打开时,Claude Code 会自动拾取它。


代理可以询问什么

一个 MCP 客户端在一次调用中查询两个仓库

一次 search_symbols 调用、一个契约符号,结果从两个仓库、以两种语言返回——而客户端两个仓库都未检出。这个工具将 diff 永远无法跨越的边界变成了寻常之事。

十八个工具,通过 Streamable HTTP 在 /mcp/ 提供服务,并使用与 /api/ 相同的 bearer token 进行认证:

工具

回答

list_repos

哪些仓库已被索引,以及每个索引的新鲜程度

list_groups

哪些仓库被归组在一起,使跨仓库问题有范围可依

find_symbol

名称在何处定义,跨所有已索引的仓库

get_symbol

定义本身,及其文件和行范围

find_callers

什么在调用它——grep 回答得很差、图却回答得精确的问题

find_callees

它调用什么,向外一跳

cross_repo_edges

跨越仓库边界的调用

query_graph

只读 Cypher,用于上方七个工具无法覆盖的问题

cross_repo_edges 是最值得理解的一个,因为它正是这个产品携带符号图的全部理由。只做 diff 的评审者——上面基准表中的每一个工具,包括图为空时的这一工具——能告诉你函数签名变了。它无法告诉你,另一个仓库中的某个服务仍在调用旧形态,因为它从未打开过那个仓库。把仓库分组一次,那个问题就变得可以回答:

> which services outside this repo call PaymentGateway.charge?

这也是为什么我们的基准排名是在低估产品,而非如实描述它:基准集是孤立的单仓库 pull request,因此没有兄弟仓库可供边跨越。这项能力真实存在,而基准看不见它——这是对基准的陈述,而不是一个你应该盲目相信的说法。把 MCP 客户端指向你自己的分组,亲自检查。

结果

Celmis 运行在 Martian Code Review Bench 的离线集上:50 个精心挑选的 pull request、173 条人工编写的黄金评论,由 LLM 评判者对照黄金集评分。在 e0db376 上、使用 gemini-3.6-flash、温度 0.1、无推理 token 的情况下测量。

评判者

F1

精确率

召回率

排名

claude-opus-4.5

47.5%

52.4%

43.4%

17 / 50

claude-sonnet-4.5

44.9%

48.0%

42.2%

17 / 50

gpt-5.2

42.7%

46.0%

39.9%

17 / 50

F1 会根据评判者不同而移动 4.8 个百分点。排名则完全不动——三位评判者下都是第十七名。三项评判中每一项都排在我们之后的:CodeRabbit(19/25/23)、每个版本的 Greptile(26–29)、Kodus(21/23/21)、Copilot、Claude Code、Gemini 和 CodeAnt。

整个运行花费 5.88 美元——每个 pull request 0.118 美元——并产生了 153 条发现,每个 PR 3.06 条(缺陷 114、安全 27、契约 6、结构 6)。

为什么这个对比是公平的。 Martian 在基准仓库中自带了对 49 个工具的评估,由同三位评判者在同样的 50 个 PR 上、对照同样的黄金集产生。我们没有为任何人重新评分:他们的行按发布原样取用,我们的行是追加的。用以下命令复现整张表:

python3 autoloop/offline_table.py anthropic_claude-sonnet-4-5-20250929

离线不是公开排行榜。 Martian 运行两套基准。公开排行榜是在线的那套——20 万个真实 pull request,按开发者实际修复的内容评分。这张表是离线的那套——50 个精心挑选的 PR,对照黄金集评分。它们衡量不同的东西,数字不可互换。“工具 X 在 Martian 上排名第一”这类说法,通常指的是在线榜单、不同的指标,或不同的评判者。

这个数字不包含什么。 所有 50 个 PR 的图都是空的(graph_status 为 null,每个的 drift 都为空),因为基准集是孤立的单仓库 pull request——没有兄弟服务让符号在其中拥有消费方。跨仓库 drift——这个产品为之携带符号图的东西——对上面的分数贡献了正好为零。这一点在这里无法衡量,我们也不据此表作此声称。要看它在真实代码上的效果,见 Test repositories。

误报审计

基准评分有一个结构性下限:评判者将我们的评论与一份有限的人工编写黄金列表匹配,因此一条标注者从未写下的正确发现,按构造就会被计为误报。我们在测量所用的提交处,逐一打开了源码中我们全部 79 条发现,并为每一条给出裁定。

在评分为误报的 79 条发现中,33 条是黄金集未包含的真实缺陷,38 条确实错误,8 条无法从代码中判定。这把本次运行的真实精确率放在了 69.7% 到 75.0% 之间,而非测得的 48.0%——但修正后的数字无法与上表中的任何一项比较,因为没有人以同样的方式审计过其他工具,而它们的误报几乎肯定含有类似比例的真实缺陷;要与其他工具比较,测得的 48.0% 才是诚实的数字,因为这是对所有人都采用同一方法所得到的数字。

38 条确实错误的发现中,有 24 条共有四个根本原因,其中没有一个是“模型太弱”——四个都关乎模型看到了什么。最大的一类是声明在同一文件中、却位于代理收到的摘录之外的标识符:一个向上 26 行的方法参数、一个第 3 行的 import、一个第 18 行的 attr_reader。

完整报告为这 79 条中的每一条给出断言、该提交处的代码、裁定、推理和永久链接,因此任何裁定都可以在同样的证据摆在面前时被提出异议。

测试仓库

上面运行中的每条评审仍然在线且公开。这些是来自真实项目的真实 pull request,fork 时保留了原历史,上面承载着 Celmis 写的行内评论:

值得先打开的:

  • keycloak#17 — Keycloak 测试存储提供者中的一个空指针解引用,以及一个恢复码索引问题

  • grafana#16 — 一次记录在 Legacy 指标下的 Storage 故障,是该文件中同一错误的三个实例之一

  • cal.diy#11 — forEach 搭配异步回调,因此删除操作是即发即忘的,外围的 try 捕获不到任何东西

  • sentry#11 — 一个 Kafka 消费者 PR 上的七条行内评论

你读到的是未经编辑的输出,包括上面审计标记为错误的发现。评分之后没有任何内容被移除。

配置

./scripts/init-env.sh 从 .env.example 写入 .env 并生成每一个密钥。示例有意让每个密钥为空:之前的版本把生成命令放在变量旁边,dotenv 文件没有行内注释,而每个复制了它的安装,都以一个打印在仓库里的主密码在运行。

设置只能通过 docker-compose.yml 中的 environment: 块到达容器——镜像不携带 .env。未在那里命名的变量,无论你的 .env 怎么说,都采用代码默认值。GET /healthz 按进程实际解析到的结果报告各评审时钟,这就是你检查什么已到达的方式。

这些时钟作为一组记录在 .env.example 中,并带有将它们绑定在一起的不变式:

REVIEW_LLM_TIMEOUT_SECONDS × (1 + RETRY_FACTOR)  ≤  REVIEW_TIMEOUT_SECONDS

上调其中一个,另一个就必须跟上;测试强制执行这一点。

Variable

Default

REVIEW_TIMEOUT_SECONDS

900

单次评审的墙钟时限;超过后后续阶段停止运行,评论会说明这一点

REVIEW_LLM_TIMEOUT_SECONDS

300

单次模型调用。如果使用较慢的推理模型,可提高到约 600

REVIEW_LLM_TIMEOUT_RETRY_FACTOR

2.0

超时后重试时限的延长倍数;设为 1.0 可禁用延长

REVIEW_MAX_DIFF_SIZE_BYTES

500000

更大的 diff 会被拒绝,而非截断

REVIEW_VERIFIER_ENABLED

false

LLM 对误报的否决机制

REVIEW_AGENT_CONCURRENCY

3

每次评审同时进行的提供商调用数

CELMIS\_JOB\_LEASE\_SECONDS

600

工作进程静默时间的上限,超过后任务可被回收

CELMIS\_DEPLOYMENT\_MODE

single_tenant

multi_tenant 可将各工作空间相互隔离


运维

docker compose logs -f api            # follow the API
docker compose exec api analyzer graph-stats <repo>   # what parsed, what did not
./scripts/backup.sh                   # Postgres + volumes
./scripts/restore.sh <archive>

管理 → 监控 显示队列深度、每个工作空间的支出以及每个代理的模型设置。用量与成本 按使用入口拆分支出,因此批量文档构建的支出不会被误认为聊天。

部署到服务器需在服务器上运行 ./scripts/deploy-on-server.sh v0.1.0:它会拉取已发布的镜像,在 Caddy 后方启动整个技术栈,并为 AGPL 页脚所链接的构建打上标记。除该机器外,无需任何凭据。如需普通虚拟机,请参阅 docs/ORACLE_CICD.md 或 docs/HETZNER.md。


本地开发

# Postgres and Qdrant from compose, everything else on the host
docker compose up -d postgres qdrant

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

alembic upgrade head
uvicorn src.api.main:app --reload --port 8000

cd web && npm install && npm run dev     # http://localhost:3000
pytest -q                # the suite
ruff check .             # lint, ratcheted at zero
cd web && npx tsc --noEmit

CLI 参考

analyzer 通过 pip install -e . 安装;在 Docker 内使用 docker compose exec api analyzer …。每个命令都支持 --help。

analyzer init

创建工作空间布局

analyzer index <path|url>

将仓库解析到图结构中

analyzer ask "<question>"

单个问题,带引用的回答

analyzer chat

交互式会话

analyzer review <provider> <repo> <pr>

评审拉取请求;--post 发布

analyzer generate

构建文档库

analyzer refresh

重新索引已更改的内容

analyzer graph-stats <repo>

按语言显示解析结果

analyzer serve

无需 Docker 的 API

analyzer review-serve

单独运行 webhook 接收器

分组子命令:analyzer repo、analyzer group、analyzer auth、analyzer mcp、analyzer scip。


架构

                      ┌──────────────┐
   GitHub / GitLab ──▶│   webhook    │──┐
   Bitbucket          └──────────────┘  │
                                        ▼
   Browser ──▶ web (Next.js) ──▶ api (FastAPI) ──▶ Postgres   jobs, policies, audit
                                     │              Qdrant     embeddings
                                     │              sandbox    untrusted execution
                                     ▼
                              model provider
                       (direct, or via a LiteLLM gateway)
  • Postgres 保存作业、策略、运行历史、支出和审计日志。持久化作业队列是一张表——出队操作是 SELECT … FOR UPDATE SKIP LOCKED,工作进程在运行期间续租其租约,而不是预先猜测一个时长。

  • Qdrant 保存嵌入向量,每个安装一个集合,并在过滤器中强制实施工作空间隔离。

  • sandbox 以独立的 uid 和独立的网络运行任何不受信任的内容——测试套件、构建——且没有数据库、没有密钥、根目录为只读。

  • LiteLLM 是可选的。同时设置 LITELLM_PROXY_URL 和 LITELLM_MASTER_KEY 后,所有调用都会通过网关路由;若任一为空,则直接使用提供商密钥。


故障排除

容器无法启动。 使用 docker compose logs <service>。API 会在启动时说明哪些可选功能不可用以及原因,而不是静默失败。

评审没有任何产出。 检查 GET /healthz 以确认解析后的时钟,然后运行 docker compose logs api | grep agent_。每个代理都会记录其耗时、模型和失败代码。

超时,而非故障。 local_timeout 表示本安装自身的截止时间先于提供商响应而到期——请提高 REVIEW_LLM_TIMEOUT_SECONDS。该情况被刻意不报告为提供商故障。

问答没有引用。 仓库可能未被索引,或已被索引但没有嵌入向量。仓库 显示每个仓库的状态;analyzer graph-stats <repo> 显示解析结果。

沙箱始终繁忙。 SANDBOX_SLOTS 决定同时运行的作业数量,是消耗内存的调节旋钮。SANDBOX_SLOT_WAIT 是调用方在被告知返回之前排队等待的时长。


项目结构

src/
  api/          FastAPI app, routers, schemas
  review/       PR review — agents, orchestrator, providers, policies
  indexing/     parsers, symbol graph, embeddings
  qa/           retrieval and answer composition
  generation/   documentation vault
  llm/          provider clients, error taxonomy, cost ledger
  sync/         git providers, the durable job queue, workers
  sandbox/      the isolated execution server
  mcp_server/   the MCP surface
  security/     redaction, patterns, log filtering
web/            Next.js UI (App Router, 16 locales)
tests/          5200+ tests
deploy/         Caddy overlay and the LiteLLM gateway config
docs/           deploy guides and the end-to-end walk-through
bench/          benchmark harness and results

来源与权利

本仓库只有一个根提交,包含约十万行——这种形态在来源扫描器眼中是来源不明的代码投放,需要解释而不是一笑了之。这里确实有一份:PROVENANCE.md 说明了许可证立场和代码来源——开发在此提交之前是私下进行的,构建、审计或复刻这里的代码都不需要这些内容。

该文件是事实记录,而非许可证。许可证是 AGPL-3.0,只有一个例外:ee/ 下的所有内容,以及任何文件名中包含 .ee. 的文件,均改由 LICENSE_EE 覆盖。ee/ 目前不包含任何产品代码——边界是在打第一个 tag 之前划定的,因为之后再划定意味着要重新询问每一位已在无附加条件的 AGPL 下提交过工作的贡献者。

此处发布的所有内容均为 AGPL,包括那些看起来商业化的部分:审计控制台、用量与支出、合规检查、安装指标。安全控制永远不会仅限企业版——审计日志是在 AGPL 下编写的,并且将始终如此。新代码应放在哪里,请参阅 CONTRIBUTING.md。

Related MCP Connectors

Related MCP Servers