- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
本指南以 Comp AI CRM 的遥测文档 docs/telemetry.md 为骨架,完整拆解这个开源 CRM 在自身安装中上报的全部数据:每天一次的install_daily聚合事件、八个一次性设置漏斗事件、四类错误事件,以及围绕"绝不泄露客户数据"设计的一系列机制——白名单属性、无 IP、行锁防重、确定性 UUID、落地页域名门控。读完你将掌握该项目的遥测数据流、每个属性的确切含义、如何关闭/审计它,以及如何为自己的安装新增一个遥测属性。
遥测是什么,以及为什么这样设计
Comp AI CRM 是一个 Agent-first 的开源 CRM(见 README.md),它内置了一套匿名使用遥测:安装会向 PostHog 上报少量聚合数据,用于作者了解这个软件被怎样部署和使用。关键前提是:这是一个存储联系人、邮箱、公司、交易金额的 CRM,遥测的设计目标从第一行起就是在收集任何数据之前先划定永不触碰的边界。
docs/telemetry.md是这份上报内容的完整清单——文档明确说明没有设置页面可以查看或控制它,"这就是该读的东西"。设计决策本身记录在adrs/的决策文档中,本文只聚焦这份清单及其在源码中的落地。
三个最核心的事实:
- 开启状态:默认开启。关闭方式是设置环境变量后重启,见下一节。
- 目标不可配置:PostHog 的项目 key 与上报 host 是 packages/telemetry/src/project.ts 中的三个常量(
POSTHOG_KEY、POSTHOG_HOST、POSTHOG_UI_HOST)。phc_前缀的 key 是只写(write-only)的——只发送事件、不读回任何数据——因此作者刻意不把它做成配置项:一个可配置变量只会让一个公开标识符看起来像机密。要 fork 到别处,就编辑该文件。 - 测试环境绝不发送:
NODE_ENV=test时遥测直接禁用(见 packages/telemetry/src/disabled.ts 第 8 行),apps/api与apps/agent的test脚本还额外设置CRM_TELEMETRY_DISABLED=1——因为规范测试允许重新赋值process.env.NODE_ENV,曾经就有这样一个测试让真实事件漏发出去。
关闭遥测:环境变量与重启
在原文档的基础上,这里给出完整的关闭方式与底层判定逻辑:
# 在仓库根目录 .env 中任选其一(或两者都设) CRM_TELEMETRY_DISABLED="1" DO_NOT_TRACK=1然后重启 API 与 Agent 进程即可。从 packages/telemetry/src/disabled.ts 的源码可以确认判定规则:
- 两个变量名被硬编码在
DISABLE_VARIABLES = ["CRM_TELEMETRY_DISABLED", "DO_NOT_TRACK"]中; - 值为
1、true、yes、on(大小写不敏感、先 trim)之一即视为关闭,其他值不算; NODE_ENV === "test"无条件返回关闭,优先级最高;- 该判定被 packages/telemetry/src/client.ts 的
send()与 apps/api/src/telemetry/rollup.service.ts 的run()在发消息前双重检查。
关闭后的行为在 apps/api/src/telemetry/telemetry.service.ts 中可见:TelemetryService.onModuleInit()若检测到禁用,仅打印一行日志便返回,不会启动任何定时器。而 rollup 在禁用时先于一切返回——不记录里程碑、不清空计数器、不认领 rollup 日。这意味着关闭期间没有任何数据被"静默消费",重新打开后漏斗依然完整(见下文"关闭不留痕迹")。
上报形态:六个硬性约束
docs/telemetry.md的 "The shape of it" 一节定义了遥测的总体形态,逐条对应源码实现:
1. 纯服务端、无自动捕获(autocapture)。posthog-node只存在于apps/api与apps/agent两个服务。没有 autocapture、没有 session replay、没有任何能触达记录数据的客户端代码。原因直白:autocapture 会把联系人姓名、邮箱、公司域名、交易金额从你的数据库里捞出来——"最简单的不持有方式就是不采集"。唯一的例外是营销站点trycrm.ai,它是网页而非安装(详见"落地页遥测"一节)。
2. 单一标识符:一个由创建数据库的 migration 生成的 UUID(install表),不绑定任何其他东西。identify()从不被调用,没有任何 person 属性,任何用户、邮箱或ALLOWED_SIGN_IN值都不会发送。在 packages/telemetry/src/client.ts 的payload()中可以看到,distinctId就是install.uuid,且每个事件都携带$process_person_profile: false——安装事件永远不会成为 person、也不会加入任何 person。
3. 频率极低:每天一个install_daily事件,加上八个一次性设置事件,再加上每个失败一个错误事件。
4. 无 IP 地址。每个事件都设置$ip: null并禁用 geoip,同时接收项目开启了anonymize_ips,地址在 ingestion 时被丢弃。这两者缺一不可:PostHog 无论 SDK 发送什么,都会从传入连接中盖上$ip,并且把 null 读作"未提供"而非"不要保留"。文档强调:如果你把这个指向别的项目,必须在那边也开启anonymize_ips。client.ts 中new PostHog(...)的配置可见disableGeoip: true与enableExceptionAutocapture: false。
5. 从不阻塞、从不抛异常。每次发送都是 fire-and-forget,包在吞掉错误的 handler 里;遥测失败只以 debug 级别记日志,其余地方不可见。client.ts的send()全链路 try/catch,capture()甚至不返回 Promise。
6. 白名单在代码里。packages/telemetry/src/allowlist.ts 的ALLOWED_PROPERTIES数组列出每一个允许发送的属性名,其余一律在事件构建前被丢弃——靠的是机制而非审查约定。permitted()的实现很干脆:遍历属性对象,凡不在ALLOWEDSet 中或值为undefined的,一律跳过。
每日事件 install_daily
install_daily是遥测的主体:每个安装每天一次,由 API 自身发出,内容全部来自分组聚合查询(counts and distributions),"永远不会是一行数据里的某个值"。
运行机制:进程内定时,无需 cron
docs/telemetry.md说明了这个事件如何做到无需外部调度:
TelephoneService(即 telemetry.service.ts)在启动时(onModuleInit)执行一次rollup.run(),之后每60 * 60 * 1000ms(一小时)再跑一次;- "认领"机制(见下)让除第一次外的所有执行都变成空操作——同一 UTC 日只发一次。
这覆盖了两种部署形态:serverless在冷启动时聚合,常驻容器靠定时器聚合。POST /internal/telemetry/rollup路由依然存在,供想要自己驱动它的平台 cron 使用——它仍然是匿名路由,因此必须携带CRON_SECRET(见 apps/api/src/telemetry/telemetry.controller.ts:用常量时间的timingSafeEquals校验Authorization: Bearer <CRON_SECRET>,未设置 secret 直接返回 503)。若不设CRON_SECRET,安装完全不受影响,报告内容一模一样。
防重发三重保险
第一层:行锁认领。packages/telemetry/src/install.ts 的claimRollup()在一个事务里对install行执行SELECT ... FOR UPDATE,再比较sameUtcDay(previous, at):若今天已发过则拒绝认领。两个 cron 同时触发也只会产生一个事件——而且第二个不会发现计数器已被抽干。
第二层:确定性 UUID。事件携带stableUuid(install.uuid, "install_daily", day)——一个 SHA-256 摘要改造成 v8 UUID。PostHog 对已见过的重复事件只摄入一次。这里的技术细节很有讲究:RFC 4122 的 v5 是 SHA-1,CodeQL 会拒绝"安装身份哈希 + 弱算法"出现在同一表达式里;项目不需要 v5 的语义,所以用了 RFC 9562 为自定义派生预留的 v8 槽位。install.ts 的stableUuid()实现可见:SHA-256 取前 16 字节,按位设置版本(0x80)与变体(0x3f | 0x80)标志后格式化为 UUID 字符串。
锁阻止两个进程竞争,确定性 id 阻止同一个进程发送两次(release 路径可能引发)。计数安装量在重复下本来就准(PostHog 的 unique 计算按"安装 × 天"),但tool_calls_total这类求和属性会被重复计算——现在不会了。
第三层:失败即归还。posthog-node在发送失败时不会 reject——它的sendImmediate会捕获错误并转而在 client 上抛出事件——所以client.ts先capture再await flush()(flush会真正抛错),并额外检查 client 的错误计数器(飞行中的任何 capture 都可能拨动它)。任一信号都视为失败。这倾向于重发一个其实已送达的 rollup(确定性 id 让重发免费),而不是消耗掉一个事件其实从未到达的"日子"(那不可恢复)。失败时rollup.service.ts调用giveBack():把lastRollupAt回退到认领前的值,并把telemetryCounter计数器写回——明天的运行不会被一次没发出去的消息压制。
安装信息
install_daily携带的通用属性(均可在ALLOWED_PROPERTIES中验证):
| Property | 含义 |
|---|---|
crm_version | 根package.json的version,每次启动重新读取(packages/telemetry/src/version.ts 的crmVersion()定位 workspace 根后解析 JSON;安装行里的版本由 install.ts 的syncVersion()在启动时更新) |
git_commit_sha | 有VERCEL_GIT_COMMIT_SHA时为其值,否则省略(commitSha()还接受GIT_COMMIT_SHA、GITHUB_SHA,并校验 7–40 位十六进制) |
days_since_install | 自首个 migration 运行以来的整天数 |
is_vercel | VERCEL是否被设置 |
node_version | 仅主版本号,如22 |
postgres_version | 仅主版本号,如17 |
members_bucket | 团队规模,分档 |
agent_model_id | Settings → General 中选择的模型,如zai/glm-5.2-fast |
agent_model_context_window | 该模型的上下文窗口(token 数) |
seed_only | 所有联系人是否都来自bun run db:seed |
能力标记:布尔值,永不带值
以下属性只表示"对应 key 是否已设置",绝不携带 key 本身、值或末四位:
cap_perplexity、cap_context_dev、cap_blob、cap_github、cap_redis、cap_agent_bridge、cap_cron_secret、cap_ai_gateway、cap_google_oauth、cap_sso_provider、cap_tracking、is_marketing。
实现上的差异点:cap_context_dev看的是AppSetting行里是否存有 context dev key;cap_sso_provider看ssoProvider行是否存在;cap_tracking看是否铸造过 tracking site id。文档特别注明:cap_context_dev一个布尔覆盖了 Agent 的 Context 两项能力(按域名查公司品牌数据、通过 LinkedIn URL 识别人),因为它们共用同一个 key。
Agent 运行数据
| Property | 含义 |
|---|---|
tool_calls | 最近 24h 的工具调用,按工具名分组 |
tool_calls_total | 上述求和 |
tool_errors | 其中返回错误的调用,按工具名分组 |
sandbox_used | 是否有任何沙箱工具(bash、grep、glob、文件读写)运行过 |
sessions_started/sessions_completed/sessions_failed | 窗口内的不同会话数 |
tools_per_session_mean | 工具调用数 ÷ 发起过调用的会话数 |
tasks_claimed/tasks_completed/tasks_retired | AgentTask计数,按kind分组 |
task_attempts_mean/task_attempts_max | 每种 kind 的尝试次数 |
budget_exhausted | 因研究预算耗尽而停止的会话数。每个会话只计一次,不是每次被拦截的工具调用都计 |
recheck_scheduled | 窗口内排队的recheck任务数 |
recheck_interval_days | 它们的间隔,按天分档 |
agent_conversations | AgentConversation行数 |
工具名会与apps/agent/agent/tools/下作者编写的工具以及 eve 自带的内建工具比对(AGENT_TOOLS与EVE_TOOLS两份常量清单见 allowlist.ts),其他一律归入other。任务 kind 与TASK_KINDS(来自@crm/db/agent-tasks)比对。值得注意:工具计数读的是 audit hook 已经写入的agentEvent行,只取事件type和工具名——AgentEvent.data本身永不发送。测试 packages/telemetry/test/allowlist.spec.ts 甚至断言AGENT_TOOLS与apps/agent/agent/tools/目录下的.ts文件名逐一对应,防止清单与实现脱节。
证据台账(Evidence Ledger)
| Property | 含义 |
|---|---|
facts_by_status | ContactFact按APPLIED/PROPOSED/DISMISSED/SUPERSEDED计数 |
facts_by_band | 按VERIFIED/PROBABLE/POSSIBLE计数 |
facts_by_method | 按工具记录的method标签计数 |
facts_by_evidence_kind | 按证据种类计数,来自 apps/agent/agent/lib/evidence.ts 的WEIGHTS映射 |
fact_dismissal_rate | 已决策事实中被人否决的比例 |
fact_decision_median_hours | 从observedAt到decidedAt的中位小时数 |
facts_superseded_within_7_days | Agent 在 7 天内改判的事实数 |
证据种类必须匹配lib/evidence.ts中的十一种(EVIDENCE_KINDS常量,测试断言其与WEIGHTS的键逐一相等);method是工具写的标签,因此只有当它匹配严格的小写 dotted-slug 形态且短于 40 字符时才发送——名字、地址、句子一律过不了正则(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/),计为other。ContactFact的value、field、evidence、sourceUrl永不发送。
CRM 数据
| Property | 含义 |
|---|---|
contacts_bucket、companies_bucket、deals_bucket、activities_bucket | 规模分档 |
contacts_by_source、companies_by_source | 按MANUAL/IMPORT/EMAIL/CALENDAR计数 |
deals_by_stage | 按DealStage计数。阶段,永远不是金额 |
activities_by_type | 按ActivityType计数 |
mailbox_sync_configured | 是否有任何MailboxSync行 |
mailbox_sync_status | 按GoogleSyncStatus计数 |
threads_ingested、messages_ingested | 窗口内到达的数量。仅计数 |
enrichment_by_status | Company按EnrichmentStatus计数 |
suppressed_domains、suppressed_contacts | 计数。从不发送域名或地址本身 |
workspace_profile_written | 是否存在WorkspaceProfile行 |
网站跟踪(Website Tracking)
| Property | 含义 |
|---|---|
tracking_domains | 白名单上有多少域名,分档。永不含主机名 |
tracking_page_views | 窗口内记录的页面浏览数。计数,不含路径 |
tracking_forms | 窗口内存储的表单提交数。计数,不含字段 |
tracking_contacts_created | 窗口内由跟踪功能创建的联系人数——即Contact.source = TRACKING,不包括挂到已存在联系人名下的提交 |
tracking_capped | 被每小时联系人上限拒绝的提交数。与CONTACT_CAP_REASON(@crm/db/tracking)精确匹配——文案是两个文件共享的常量,而不是某个文件可能改写的措辞 |
tracking_paused | 采集是否暂停 |
设置漏斗:八个一次性事件
以下八个事件每个安装一生各发送一次,且各自携带"事件发生时刻"的时间戳而非"被发现时刻"——所以即使发送它们的 nightly job 是后来才跑的,漏斗读起来依然真实:
migrations_applied → first_sign_in → google_oauth_configured → first_mailbox_sync → first_non_seed_contact → first_agent_task_claimed → first_agent_task_completed → first_fact_applied除通用属性(版本、安装天数、是否 Vercel)外不携带任何自有属性。几个值得展开的实现细节:
google_oauth_configured是唯一没有行可读时间的步骤:两个环境变量(GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET)不会留下时间戳。它的时间戳取第一个 Googleaccount行(配置必然先于账号存在),否则取 sweep 第一次看到变量被设置的时刻。该 sweep 在每次 API 启动时都跑,而不仅限于 cron——所以一个"带着配置抵达"的安装会在首次启动时就被盖章。first_fact_applied取人类接受的任一事实的最早decidedAt,或 Agent 自己应用的最早observedAt,取先者。被应用后又 supersede 的事实依然算数——"后来被替换"不改变"第一次应用发生过"。- 先认领、后发送;发送失败即交还。认领就是插入本身:
telemetryMilestone.step是主键,两个进程同时 sweep 时只有插入成功(createMany返回count === 1)的那个会发送;发送失败则删除该行(forgetMilestone),下次 sweep 重试,而不是永久丢失该步骤。
文档还记录了一个真实的演进教训:这个顺序与最初版本相反——最初是"先发送后记录",理由是"重复只是舍入误差,而丢失步骤无法恢复"。但那个顺序让重复成为常态而非罕见(启动 sweep 和 rollup 都会调用sweep(),曾有安装把first_fact_applied发了六次)。改为先认领,代价是"认领后、发送前崩溃"会丢失该步骤——但每个事件还带stableUuid(install.uuid, step)的确定性 UUID,真正被发送两次也会被 PostHog 只摄入一次。遥测关闭时什么都不记录。
错误事件:只报类,不报内容
错误遥测的规则是:错误类别 + 来源。绝不发送消息、堆栈、payload——"我们的错误里会带联系人字段"。
| 事件 | 属性 |
|---|---|
agent_error | error_class、tool、task_kind、error_source(tool/turn/session) |
sync_error | error_class、sync_source(gmail/calendar/outlook)、error_source(google_sync/microsoft_sync/mailbox_sync) |
api_error | error_class、route、status_code |
model_error | error_class、model_id |
从 packages/telemetry/src/events.ts 与 allowlist.ts 可以看到这些事件的构建函数,其约束值得细读:
sync_error的error_source由sync_source推导而非直接传入——因为同一条 mailbox 管道会分派给两个 provider,直接传值会让 Outlook 失败被报告成 Google 失败。非三种 source 的发送为other,其error_source固定为mailbox_sync(permittedSyncErrorSource)。route是路由模式——/internal/sync/google、/trpc/contacts.list——绝不带参数的 URL。它必须匹配严格路径形态(/^\/[A-Za-z0-9/_:.*-]*$/,最长 120 字符),否则为other。error_class取Error实例的name/constructor.name、字符串本身或对象上的code;必须匹配/[A-Za-z][A-Za-z0-9_.-]*/且短于 64 字符,否则为other。测试 allowlist.spec.ts 验证了new TypeError("ada@example.test is not a fn")只会送出TypeError。
apps/agent/agent/hooks/telemetry.ts是 tool、turn、session 三类 Agent 失败的入口。沙箱内部(apps/agent/agent/sandbox)完全没有插桩——它按设计运行deny-all出站策略,里面的 capture 会挂起然后失败。
落地页遥测:trycrm.ai 与域名门控
trycrm.ai是仓库里唯一运行posthog-js的地方:陌生人阅读营销页面的 web 分析和 session replay。它是网站而非安装——页面背后没有数据库,记录也在它触达不到的地方。对 CRM 本身而言,以上所有承诺依然成立。
它无法在你的主机名上运行。apps/app/lib/analytics.ts 的analyticsAllowed()把window.location.hostname与两个字面量(trycrm.ai、www.trycrm.ai)比对;posthog-js藏在这道检查之后的动态import()后面(apps/app/components/landing/analytics.tsx)。即使你设置IS_MARKETING="true"并在自己的域名上托管同一页面,SDK 也永远不会被下载——不是"发送后被丢弃",而是从未加载。门控用 hostname 而非IS_MARKETING,是因为后者恰恰是自托管者最可能去设置的那个旗标。PostHog 那一侧也拒绝:接收项目的recording_domains声明了同样的两个 origin,任何带着该 key 出现的其他页面都会被拒收 replay 和 heatmap。
它与安装共享同一个项目、key 和 host,但两者绝不混合:落地页发送$pageview、$pageleave、$autocapture、$web_vitals以及下面两个 CTA 事件,是关于浏览器的;安装发送install_daily和漏斗,是关于数据库的。安装事件带$process_person_profile: false,永远不会成为 person 或加入任何 person。
两个 CTA 事件
| 事件 | 触发时机 |
|---|---|
setup_prompt_copied | 设置提示真正写入剪贴板之后——被拒绝的剪贴板不算复制成功 |
github_star_clicked | 点击了Star on GitHub按钮。只是"前往 GitHub 的点击",不是"已加星"——是否真的加星发生在看不到的页面上 |
两者都带一个属性cta_location——hero或closing——因为两个按钮在页面上各出现两次,访客到达的是哪一个,是唯一值得知道的点。它们经由captureLanding发送,与init一样在 hostname 门控与动态import()之后;在其他主机名上 SDK 根本不存在,点击也不会被发送。两个发送都是 fire-and-forget,不延迟复制或导航。
anonymize_ips保持开启是有代价的:PostHog 在 geoip 之前就丢弃地址,所以落地页没有国家/地区/城市数据。这正是让上面"无 IP"承诺对每个安装成立的那个设置——安装的匿名性优先于营销地图。想要两者兼得,唯一的办法是拆成两个独立项目。
ALLOWED_PROPERTIES管不到这些事件——它守卫的是packages/telemetry构建的内容,而这里是浏览器 SDK 自己的。发送的是普通 web 分析:URL、referrer、UTM 参数、浏览器、设备、屏幕尺寸、会话 ID。Replay 默认掩蔽所有输入,而这个页面没有可输入的字段。
永不发送清单
以下内容在任何情况下都不会被发送:
- 任何
Contact或User的姓名、邮箱、头衔或照片 - 任何
Company的名称、域名、行业或地点 ContactFact的.value、.sourceUrl、.evidence、.fieldAgentTask.reason和AgentTask.outcome——两者都是 Agent 针对具名人物写的自由文本ContactBrief内容、WorkspaceProfile的.website、.narrative、.sectionsEmailThread与EmailMessage的主题或正文、CalendarEvent标题、CalendarAttendee行Deal名称和金额(阶段分布可以,金额不可以)AgentEvent.data、AgentConversation内容、prompt、completion、推理轨迹ALLOWED_SIGN_IN、AppSetting.contextDevApiKey、任何 key、secret、token 或连接串SuppressedDomain与SuppressedContact的值——只发计数- IP 地址:设
$ip: null并禁用 geoip
最后一项要特别说明:仅仅两个属性并不能达成"无 IP",因为 PostHog 会在 ingestion 时从连接里填上$ip——真正丢弃它的是接收项目上开启的anonymize_ips。
代码位置速查
| 位置 | 职责 |
|---|---|
| packages/telemetry | 全仓库唯一posthog-node导入处。白名单、安装访问器、事件构建器 |
install表 | UUID、版本、上次 rollup 时间。一行,由 migration 创建 |
telemetryMilestone表 | 哪些漏斗步骤已触发 |
telemetryCounter表 | 唯一一个没有其他行记录的运行时数字——budget_exhausted——每次 rollup 抽干,发送失败则写回 |
| packages/telemetry/src/project.ts | key、ingest host、UI host。三个常量、零导入,所以浏览器也能读它们 |
| apps/api/src/telemetry | 每日 rollup、漏斗 sweep、启动+每小时定时器、可选 cron 路由 |
| apps/agent/agent/hooks/telemetry.ts | tool、turn、session 失败 |
| apps/app/lib/analytics.ts | 落地页允许上报的两个主机名 |
| apps/app/components/landing/analytics.tsx | 仓库里唯一的posthog-js导入,位于门控与动态import()之后。init在挂载时,captureLanding用于两个 CTA |
apps/agent/agent/sandbox内无任何插桩——它按设计deny-all出站,里面的 capture 只会挂起后失败。
新增一个遥测属性:三步走
文档给出了清晰的流程,结合源码可以展开为完整的可操作步骤:
- 把属性名加入 packages/telemetry/src/allowlist.ts 的
ALLOWED_PROPERTIES。在那之前,它会被permitted()静默丢弃——这是硬闸门。 - 在本清单(
docs/telemetry.md的表格)中补一行,说明它是什么。 - 如果它是开放键集合——一个工具名、一个 method、一条 route——为它写一个
permitted*函数(如permittedTool、permittedMethod、permittedRoute、permittedSyncSource、permittedTaskKind、permittedEvidenceKind、permittedModelId、permittedErrorClass),让键本身也受约束,而不只是属性名受约束。
配套的测试文件 packages/telemetry/test/allowlist.spec.ts 展示了这套机制如何被验证:白名单外的属性(如contact_email: "ada@example.test")被丢弃、工具清单与apps/agent/agent/tools/目录逐一对应、证据种类镜像lib/evidence.ts的WEIGHTS、错误类只取类名不取消息、bucket()永不报告精确规模(1和9都归入1-9,1_000_000归入25000+)、dayBucket()把间隔分档(7天归入2-7,365归入180+)。新增属性时,为它补充同样的测试是保持这份隐私承诺可审计的最佳实践。
这份遥测体系的设计哲学可以概括为一句话:聚合到的都是分布,发送出去的都不是记录。分档(bucket)替代精确值、白名单替代约定、行锁与确定性 UUID 替代去重服务、hostname 门控替代环境变量信任——每一层都在回答同一个问题:"如果一个恶意或粗心的读取者拿到了这份事件流,他能从中复原出任何一条客户记录吗?"答案被 docs/telemetry.md 的"永不发送清单"和源码里的每个permitted*函数钉死在了代码里。
- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
相关推荐
Agent Orchestrator 产品遥测(Telemetry)机制全解:数据边界、隐私设计与关闭指南
Agent Orchestrator 产品遥测(Telemetry)机制全解:数据边界、隐私设计与关闭指南 产品遥测是 Agent Orchestrator(A
gRPC-Go 如何给 status 错误附加自定义 details 并在客户端读取?
gRPC Go 如何给 status 错误附加自定义 details 并在客户端读取? 当你的 gRPC Go 服务需要向客户端返回错误时,光靠一个错误码和描述
后端前端CRM人工智能AI AgentiFixAi 安全策略与隐私设计全解析:漏洞报告、密钥脱敏与遥测机制
iFixAi 安全策略与隐私设计全解析:漏洞报告、密钥脱敏与遥测机制 iFixAi 是一个面向 AI Agent 的独立审计框架,可在 120 秒内回答"Age
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考