
1. 项目概述为什么一个“术语库”值得单独建系统“软件工程术语库·系统与工程化篇”——光看标题很多人第一反应是“不就是个词典网上搜一下不就完了”我当年也这么想。直到在一家做工业物联网平台的公司带团队连续三个月被三个不同角色反复追问同一类问题测试同学问“这个PR里的‘可观测性’到底指日志、指标还是链路追踪文档里没写清楚”运维同事指着CI/CD流水线报错说“Pipeline failed at ‘staging deploy’阶段但‘staging’在我们环境里到底是预发还是灰度SRE手册和开发Wiki说法不一致”更头疼的是新来的应届生在需求评审会上听到“API契约先行”“服务网格化治理”“Flink作业的Exactly-Once语义保障”当场眼神放空会后追着我问“老师这些词在咱们项目里具体对应哪几行代码、哪个配置文件、哪张拓扑图”这才意识到问题从来不在“有没有定义”而在于“定义是否能闭环落地”。我们有Confluence词条、有GitBook文档、有Jira字段说明但它们彼此割裂Confluence里写的“微服务拆分粒度”是理论原则GitBook里“API网关路由规则”是配置示例Jira里“服务注册中心选型”是决策记录——三者之间没有锚点无法交叉引用更无法随代码变更自动校验。当一个团队从20人扩张到80人当系统从单体演进到包含Flink实时计算、K8s编排、Service Mesh治理的混合架构时“术语”就不再是文字游戏而是系统认知对齐的基础设施。这个术语库不是静态词典它是一套嵌入研发流程的“活体知识系统”。它把“系统”和“工程化”这两个抽象概念具象成可检索、可关联、可验证、可演进的实体。比如搜索“CI/CD”你看到的不只是定义而是关联的代码仓库.gitlab-ci.yml中实际使用的stage命名规范关联的配置模板Jenkinsfile里env变量注入逻辑的版本快照关联的监控指标Prometheus中pipeline_duration_seconds的SLI计算公式关联的故障案例某次因缓存策略未同步导致部署失败的根因分析报告关联的培训视频新人入职第三天必看的“如何读懂你的第一个Pipeline日志”。它解决的不是“不知道这个词”而是“知道这个词却不知道在本项目里该怎么用、用错了会怎样、谁负责维护它”。所以它必须是“系统”的——有数据模型、有状态管理、有访问控制它必须是“工程化”的——能通过API被其他工具调用能被CI流水线自动校验能随代码提交触发术语一致性检查。这正是标题中“系统与工程化篇”的真实分量它把知识管理从行政事务升级为研发效能的核心组件。2. 内容整体设计与思路拆解为什么拒绝“维基百科式”堆砌很多团队尝试过建术语库最后沦为“僵尸Wiki”初期热情高涨填了50个词条半年后无人更新搜索结果全是过期链接。根本原因在于设计思路上的致命偏差——把术语库当成“内容产出”而非“系统能力”。我们的设计彻底反其道而行之先定义系统行为再填充内容先确保工程化管道畅通再追求词条数量。整个架构围绕四个核心原则展开2.1 原则一术语即实体必须具备唯一身份标识URI传统Wiki里“API”这个词可能在“架构设计”“测试规范”“安全策略”三个页面重复出现每次定义略有差异。我们强制要求每个术语必须生成全局唯一URI格式为https://terms.company.com/system/engineering/api。这个URI不是页面地址而是术语的数字身份证。它背后绑定权威定义源该术语的原始出处如OpenAPI 3.0规范第4.6.2条或公司《API治理白皮书》v2.1第3章上下文约束在本项目中适用的边界条件例如“此处API特指RESTful风格HTTP接口不包含gRPC或GraphQL”生命周期状态draft草案、active生效、deprecated已弃用、replaced_by被xxx替代。提示URI设计时预留了命名空间前缀。/system/代表系统架构层术语如Service Mesh、Sidecar/engineering/代表工程实践层术语如Feature Flag、Canary Release。这样未来扩展“安全篇”“数据篇”时无需重构URL体系。2.2 原则二关系驱动拒绝孤立词条一个术语的价值70%取决于它与其他术语的连接。我们定义了六种核心关系类型每种都对应明确的业务动作depends_on表示强依赖如“CI/CD流水线” →depends_on→ “GitLab Runner集群”当被依赖项状态变更如Runner版本升级自动触发上游术语的兼容性检查implements表示实现关系如“可观测性” →implements→ “OpenTelemetry SDK”点击即可跳转到SDK集成文档conflicts_with表示冲突关系如“蓝绿部署” →conflicts_with→ “数据库主从切换”避免架构师在方案设计时踩坑example_of表示实例化如“API网关” →example_of→ “Kong企业版v3.4”关联具体产品版本和配置快照evolves_to表示演进路径如“单体应用” →evolves_to→ “领域驱动微服务”记录技术决策的历史脉络validated_by表示验证方式如“服务熔断” →validated_by→ “混沌工程实验报告#2023-Q3”让抽象概念落地为可测量的行为。这种关系网络不是人工维护的而是通过解析代码注释、CI配置、架构图元数据自动生成。例如扫描所有pom.xml文件发现artifactIdspring-cloud-starter-circuitbreaker-resilience4j/artifactId系统自动建立“熔断器”术语与Resilience4j库的implements关系并关联其Maven坐标。2.3 原则三工程化即API化一切皆可编程术语库的终极价值不在于人看而在于机器用。我们提供三层API能力读取层标准RESTful APIGET/terms/{id}返回结构化JSON包含定义、关系、状态、变更历史。前端文档站、IDE插件、ChatOps机器人均可调用验证层Webhook APIPOST/validate接收代码片段或配置文件返回术语一致性报告。例如提交一个.gitlab-ci.ymlAPI自动检测其中staging、production等环境名是否符合术语库定义的命名规范是否存在未声明的stage写入层受控写入APIPUT/terms/{id}/state仅限CI流水线调用。当某次合并请求MR引入新术语如新增/feature-flag-service流水线执行curl -X PUT https://terms.company.com/terms/feature-flag-service/state -d {state:active}术语库自动创建词条并关联MR链接。注意写入API不接受自由文本。所有新术语必须通过预设Schema提交Schema强制要求填写definition_source来源、context_scope适用范围、owner_team责任团队。这杜绝了“谁都能随便造词”的混乱。2.4 原则四闭环反馈术语必须参与研发循环最危险的术语是“死术语”——定义完美但无人使用。我们设计了三个强制闭环点代码提交时Git Hooks拦截含术语关键词的commit message如feat(api-gateway): add rate-limiting policy自动关联术语库中api-gateway词条生成变更摘要文档发布时Confluence插件扫描新发布页面识别术语URI链接若链接失效或指向deprecated状态阻断发布并提示修复故障复盘时Incident Report模板强制要求填写“涉及的关键术语”复盘会议结论自动更新对应术语的lessons_learned字段并关联到conflicts_with或validated_by关系。这套设计让术语库从“信息仓库”变成“研发神经中枢”。它不生产知识但确保知识在正确的时间、以正确的形式、触达正确的对象。当你看到一个新工程师第一次提交PR就准确使用canary-release而非beta-deploy就知道系统开始起效了。3. 核心细节解析与实操要点从零搭建术语库的硬核细节搭建术语库不是搭个Wiki网站而是构建一套轻量级知识操作系统。我们选择的技术栈极度克制PostgreSQL FastAPI Vue3拒绝任何重型CMS或知识图谱平台。原因很简单工程化系统的第一要义是可维护性而非炫技。下面拆解最关键的五个实操细节每个都来自踩坑后的血泪经验。3.1 数据模型设计为什么用“术语-关系-上下文”三表结构很多团队直接用NoSQL存词条结果查询关系时性能崩盘。我们坚持关系型数据库核心表结构只有三张表名字段关键说明termsid(UUID),uri(TEXT, unique),name(TEXT),definition(TEXT),status(ENUM: draft/active/deprecated),source_uri(TEXT),owner_team(TEXT)术语主表uri是全局唯一键source_uri指向权威定义源如RFC链接或内部文档IDrelationsid(UUID),from_term_id(UUID),to_term_id(UUID),relation_type(ENUM),confidence(FLOAT, 0.0-1.0),evidence_source(TEXT)关系表confidence字段记录关系可信度人工标注0.9自动解析0.7evidence_source存证据来源如“解析pom.xml第12行”contextsid(UUID),term_id(UUID),context_type(ENUM: code/config/doc),context_ref(TEXT),valid_from(TIMESTAMP),valid_to(TIMESTAMP)上下文表context_ref存具体引用如“git commit hash: a1b2c3d”或“confluence page ID: 12345”valid_to支持时间有效性如“此定义仅适用于v2.0-v2.3版本”实操心得contexts表的设计是成败关键。早期我们只存“文档链接”结果当Confluence页面被重命名或迁移所有上下文失效。改为存page IDspace key后即使URL变化仍可通过Confluence API反查。同理代码上下文存commit hash而非分支名确保术语定义与特定代码版本强绑定。3.2 URI生成规则如何避免“术语别名”灾难“API”“接口”“endpoint”“service contract”在不同团队可能指同一概念。我们制定严格URI生成规则基础规则全部小写用连字符-分隔单词禁止缩写ci-cd而非cidfeature-flag而非ff消歧规则当存在多义词时用上下文前缀限定。例如api-restfulRESTful HTTP接口api-grpcgRPC服务接口api-internal内部服务间调用协议演进规则旧术语弃用时不删除而是创建replaced_by关系并在新URI中体现版本。如ci-cd-v1→replaced_by→ci-cd-v2新URI为ci-cdv2成为默认。踩过的坑曾允许团队自定义URI结果出现api、apis、rest-api三个URI指向同一概念。后期清洗耗时两周强制重定向导致所有历史链接失效。教训URI是契约必须由中央系统统一分配禁止自由发挥。3.3 自动化关系抽取如何让代码“自己说话”关系不能全靠人工维护。我们开发了轻量级解析器针对三类高频场景Maven/Gradle依赖分析扫描pom.xml或build.gradle提取groupId、artifactId、version。匹配规则若artifactId含circuitbreaker自动建立circuit-breaker术语与该库的implements关系若groupId为org.springframework.cloud且artifactId含gateway建立api-gateway术语与Spring Cloud Gateway的example_of关系。实测效果覆盖85%的框架级术语关联误报率3%主要因自定义artifactId命名不规范。CI配置文件解析解析.gitlab-ci.yml或Jenkinsfile提取stages、variables、before_script等块。例如发现stages: [build, test, staging, production]自动为staging、production创建术语并建立ci-cd术语的depends_on关系发现variables: { CI_REGISTRY_IMAGE: $CI_REGISTRY/group/project }建立ci-registry术语与Docker Registry的example_of关系。技巧用正则匹配比AST解析更鲁棒。CI配置语法灵活AST易因缩进或注释崩溃而/stages:\s*\[([^\]])\]/这类正则稳定得多。代码注释标记在Java/Python代码中支持term标记/** * 熔断器配置term circuit-breaker * 启用后当错误率超50%持续30秒进入OPEN状态term circuit-breaker-state */ public class ResilienceConfig { ... }解析器提取term后建立代码位置与术语的validated_by关系。注意标记必须用英文术语名中文注释不影响解析。3.4 权限与治理谁有权修改术语如何防止“定义权争夺战”术语定义权是组织权力的映射。我们采用“三层治理模型”定义层Owner每个术语必须指定owner_team如backend-platform该团队拥有最终定义权。修改definition或status需该团队负责人审批关系层Curator设立跨团队“术语管家”角色Curator负责审核关系创建如conflicts_with需Curator确认确保关系不引发架构冲突使用层Consumer所有开发者可自由提交context如在代码中添加term但无权修改定义。权限控制通过GitOps实现所有术语数据存于私有Git仓库terms-dbterms表数据为YAML文件/terms/ci-cd.yamlrelations为CSV。修改术语需提MRCI流水线自动运行检查YAML语法验证uri格式合规检查owner_team是否存在于公司组织架构API若修改status为deprecated强制填写replaced_by字段。只有全部检查通过MR才可合并。这比RBAC系统更透明——所有变更留痕审批记录即Git提交历史。3.5 与现有工具链集成如何让术语库“隐身”于日常开发最成功的集成是开发者感觉不到它的存在。我们做了三件事VS Code插件安装后光标悬停在term circuit-breaker上右侧弹出术语定义、关系图、最新变更摘要。按CtrlClick直接跳转到术语库网页。插件不联网所有数据随插件包预装离线可用。GitLab MR模板在.gitlab/issue_templates/Architecture-Review.md中预置字段## 关键术语影响 - 新增术语[ ] api-rate-limiting - 修改术语[ ] ci-cd调整staging环境定义 - 冲突术语[ ] database-migration与当前灰度策略冲突提交MR时CI自动调用术语库API生成术语影响报告嵌入MR评论区。Confluence宏插入{term:ci-cd}渲染为超链接鼠标悬停显示定义摘要。宏后台调用术语库API若术语status为deprecated自动加红色删除线并提示替代方案。关键经验集成点必须选在开发者无额外操作成本的位置。不要让他们“去术语库查一下”而要让术语“主动出现在他们眼前”。VS Code插件的采用率从0%飙升到92%就因为“悬停即见”比打开浏览器快10倍。4. 实操过程与核心环节实现手把手完成术语库最小可行系统MVP现在让我们把前面所有设计落地为一个可运行的最小可行系统MVP。目标2小时内从零开始部署一个支持术语创建、关系关联、API查询的终端系统。全程使用开源工具无云服务依赖所有代码可直接克隆运行。4.1 环境准备三步极简初始化我们放弃Docker Compose的复杂编排采用最朴素的本地启动方式确保新手零障碍安装PostgreSQL 15macOSbrew install postgresql然后brew services start postgresqlUbuntusudo apt-get install postgresql-15Windows下载 EnterpriseDB installer 勾选“Initialize database cluster”。验证psql --version输出psql (PostgreSQL) 15.x。创建数据库与用户# 登录psql默认用户postgres psql -U postgres # 创建数据库 CREATE DATABASE terms_db; # 创建专用用户密码设为terms123 CREATE USER terms_user WITH PASSWORD terms123; # 授权 GRANT ALL PRIVILEGES ON DATABASE terms_db TO terms_user; \q初始化表结构创建init.sql文件粘贴以下SQL-- 切换到terms_db数据库 \c terms_db -- 创建terms表 CREATE TABLE terms ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), uri TEXT UNIQUE NOT NULL, name TEXT NOT NULL, definition TEXT NOT NULL, status VARCHAR(20) CHECK (status IN (draft, active, deprecated)) DEFAULT draft, source_uri TEXT, owner_team TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 创建relations表 CREATE TABLE relations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), from_term_id UUID NOT NULL REFERENCES terms(id) ON DELETE CASCADE, to_term_id UUID NOT NULL REFERENCES terms(id) ON DELETE CASCADE, relation_type VARCHAR(50) NOT NULL, confidence FLOAT CHECK (confidence BETWEEN 0.0 AND 1.0) DEFAULT 0.8, evidence_source TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 创建contexts表 CREATE TABLE contexts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), term_id UUID NOT NULL REFERENCES terms(id) ON DELETE CASCADE, context_type VARCHAR(20) CHECK (context_type IN (code, config, doc)) NOT NULL, context_ref TEXT NOT NULL, valid_from TIMESTAMP WITH TIME ZONE DEFAULT NOW(), valid_to TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 创建索引提升查询性能 CREATE INDEX idx_terms_uri ON terms(uri); CREATE INDEX idx_relations_from ON relations(from_term_id); CREATE INDEX idx_relations_to ON relations(to_term_id); CREATE INDEX idx_contexts_term ON contexts(term_id);执行psql -U terms_user -d terms_db -f init.sql提示Windows用户若gen_random_uuid()报错先执行CREATE EXTENSION IF NOT EXISTS pgcrypto;再运行init.sql。4.2 后端API用FastAPI实现核心CRUD创建main.py这是整个系统的灵魂from fastapi import FastAPI, HTTPException, Depends, status from pydantic import BaseModel, Field from typing import List, Optional import psycopg2 from psycopg2.extras import RealDictCursor import os from datetime import datetime # 数据库连接配置 DB_CONFIG { host: localhost, database: terms_db, user: terms_user, password: terms123 } app FastAPI(titleSoftware Engineering Terms API, version0.1) # 数据模型 class TermBase(BaseModel): uri: str Field(..., description术语URI全局唯一如 ci-cd) name: str Field(..., description术语名称) definition: str Field(..., description权威定义) status: str Field(draft, description状态: draft/active/deprecated) source_uri: Optional[str] Field(None, description定义来源URI) owner_team: Optional[str] Field(None, description责任团队) class TermCreate(TermBase): pass class TermResponse(TermBase): id: str created_at: datetime updated_at: datetime class RelationCreate(BaseModel): from_term_uri: str Field(..., description源术语URI) to_term_uri: str Field(..., description目标术语URI) relation_type: str Field(..., description关系类型) confidence: float Field(0.8, ge0.0, le1.0) evidence_source: Optional[str] None def get_db(): conn psycopg2.connect(**DB_CONFIG) try: yield conn finally: conn.close() app.post(/terms/, response_modelTermResponse, status_codestatus.HTTP_201_CREATED) def create_term(term: TermCreate, db: psycopg2.extensions.connection Depends(get_db)): cursor db.cursor(cursor_factoryRealDictCursor) try: cursor.execute( INSERT INTO terms (uri, name, definition, status, source_uri, owner_team) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id, created_at, updated_at , (term.uri, term.name, term.definition, term.status, term.source_uri, term.owner_team) ) row cursor.fetchone() db.commit() return {**term.dict(), id: str(row[id]), created_at: row[created_at], updated_at: row[updated_at]} except psycopg2.IntegrityError as e: if unique constraint in str(e): raise HTTPException(status_code400, detailfURI {term.uri} already exists) raise HTTPException(status_code400, detailstr(e)) finally: cursor.close() app.get(/terms/{uri}, response_modelTermResponse) def get_term(uri: str, db: psycopg2.extensions.connection Depends(get_db)): cursor db.cursor(cursor_factoryRealDictCursor) try: cursor.execute(SELECT * FROM terms WHERE uri %s, (uri,)) row cursor.fetchone() if not row: raise HTTPException(status_code404, detailTerm not found) return dict(row) finally: cursor.close() app.post(/relations/) def create_relation(relation: RelationCreate, db: psycopg2.extensions.connection Depends(get_db)): cursor db.cursor() try: # 先验证源术语和目标术语存在 cursor.execute(SELECT id FROM terms WHERE uri %s, (relation.from_term_uri,)) from_row cursor.fetchone() if not from_row: raise HTTPException(status_code404, detailfSource term {relation.from_term_uri} not found) cursor.execute(SELECT id FROM terms WHERE uri %s, (relation.to_term_uri,)) to_row cursor.fetchone() if not to_row: raise HTTPException(status_code404, detailfTarget term {relation.to_term_uri} not found) cursor.execute( INSERT INTO relations (from_term_id, to_term_id, relation_type, confidence, evidence_source) VALUES (%s, %s, %s, %s, %s) , (from_row[0], to_row[0], relation.relation_type, relation.confidence, relation.evidence_source) ) db.commit() return {message: Relation created successfully} finally: cursor.close() # 启动命令uvicorn main:app --reload安装依赖并启动pip install fastapi uvicorn psycopg2-binary python-dotenv uvicorn main:app --reload --port 8000访问http://localhost:8000/docsSwagger UI已就绪4.3 创建首个术语CI/CD的完整生命周期演示现在用API亲手创建第一个术语体验闭环流程创建ci-cd术语POSThttp://localhost:8000/terms/{ uri: ci-cd, name: CI/CD, definition: 持续集成与持续交付Continuous Integration and Continuous Delivery是一种软件工程实践通过自动化构建、测试和部署流程缩短从代码提交到生产环境发布的周期。, status: active, source_uri: https://martinfowler.com/articles/continuous-integration.html, owner_team: devops-platform }返回201获得id。创建gitlab-runner术语POSThttp://localhost:8000/terms/{ uri: gitlab-runner, name: GitLab Runner, definition: GitLab Runner 是 GitLab CI/CD 的执行器负责拉取代码、运行脚本、上传产物。, status: active, source_uri: https://docs.gitlab.com/runner/, owner_team: devops-platform }建立关系POSThttp://localhost:8000/relations/{ from_term_uri: ci-cd, to_term_uri: gitlab-runner, relation_type: depends_on, confidence: 0.95, evidence_source: 公司CI/CD架构图 v2.1 }验证查询GEThttp://localhost:8000/terms/ci-cd返回JSON中包含id、uri、definition以及relations字段需在API中补充查询逻辑此处为简化省略。实操心得首次创建时务必用curl命令行验证而非仅依赖Swagger UI。因为UI可能缓存旧Schema。真正的工程化始于对底层协议的掌控感。4.4 前端展示Vue3极简界面50行代码搞定创建index.html一个纯前端页面无需构建工具!DOCTYPE html html head titleTerms Explorer/title script srchttps://unpkg.com/vue3/dist/vue.global.js/script style body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto; margin: 2rem; } .term-card { border: 1px solid #e0e0e0; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; } .relation { background-color: #f8f9fa; padding: 0.5rem; margin: 0.5rem 0; border-left: 3px solid #007bff; } /style /head body div idapp h1软件工程术语库 · 系统与工程化篇/h1 input v-modelsearchUri keyup.enterfetchTerm placeholder输入URI搜索如 ci-cd / button clickfetchTerm搜索/button div v-ifloading加载中.../div div v-else-iferror stylecolor: red;{{ error }}/div div v-else-ifterm classterm-card h2{{ term.name }} (code{{ term.uri }}/code)/h2 pstrong定义/strong{{ term.definition }}/p pstrong状态/strongspan :style{color: term.status active ? green : term.status deprecated ? red : orange}{{ term.status }}/span/p pstrong来源/stronga :hrefterm.source_uri target_blank{{ term.source_uri }}/a/p h3相关关系/h3 div v-forrel in relations :keyrel.id classrelation strong{{ rel.relation_type }} → /strong a :href#rel.to_term_uri click.preventloadRelated(rel.to_term_uri){{ rel.to_term_uri }}/a (置信度: {{ rel.confidence }}) /div /div div v-else p请输入URI搜索术语例如codeci-cd/code, codeapi-gateway/code, codefeature-flag/code/p /div /div script const { createApp, ref, onMounted } Vue; createApp({ setup() { const searchUri ref(ci-cd); const term ref(null); const relations ref([]); const loading ref(false); const error ref(); const fetchTerm async () { loading.value true; error.value ; try { const res await fetch(http://localhost:8000/terms/${searchUri.value}); if (!res.ok) throw new Error(HTTP ${res.status}); term.value await res.json(); // 模拟获取关系实际应调用 /relations?fromuri API relations.value [ { id: 1, relation_type: depends_on, to_term_uri: gitlab-runner, confidence: 0.95 }, { id: 2, relation_type: implements, to_term_uri: openapi-spec, confidence: 0.8 } ]; } catch (e) { error.value 获取失败: e.message; term.value null; relations.value []; } finally { loading.value false; } }; const loadRelated (uri) { searchUri.value uri; fetchTerm(); }; onMounted(() { fetchTerm(); }); return { searchUri, term, relations, loading, error, fetchTerm, loadRelated }; } }).mount(#app); /script /body /html双击打开index.html一个可交互的术语浏览器诞生搜索ci-cd看到定义、状态、关系点击gitlab-runner自动加载其详情。这就是MVP的全部力量——用最少的代码验证最核心的价值。4.5 工程化接入让CI流水线自动维护术语最后一步让术语库真正“活”起来。在.gitlab-ci.yml中添加一个作业当docs/architecture.md更新时自动同步术语stages: - validate - deploy # 术语同步作业 sync-terms: stage: validate image: curlimages/curl:latest script: - | # 从architecture.md中提取所有term标记 TERMS$(grep -o term [^[:space:]]* docs/architecture.md | sed s/term //g | sort -u | tr \n ) echo 发现术语: $TERMS # 为每个术语创建或更新 for term in $TERMS; do # 检查术语是否存在 if ! curl -s -o /dev/null -w %{http_code} http://terms-api:8000/terms/$term | grep -q 200; then echo 创建新术语: $term curl -X POST http://terms-api:8000/terms/ \ -H Content-Type: application/json \ -d {\uri\:\$term\,\name\:\$term\,\definition\:\自动生成的术语请完善定义\,\status\:\draft\,\owner_team\:\arch-team\} fi done only: - main关键点terms-api是Docker网络中的服务名。在docker-compose.yml中将FastAPI服务命名为terms-api并与GitLab Runner共享网络。这样流水线就能直接调用内部API无需暴露公网。至此一个具备术语管理、关系关联、API查询、前端展示、CI集成的完整术语库MVP已在你本地运行。它不华丽但足够坚实它不庞大但直击要害。记住工程化的起点永远是解决一个具体、微小、可验证的痛点。5. 常见问题与排查技巧实录那些没人告诉你的坑在多个