独立产品 AI 代码仓库管理:多项目共享 AI 能力的架构设计 独立产品 AI 代码仓库管理多项目共享 AI 能力的架构设计一、AI 能力碎片化的管理困境每个项目都在重复造轮子独立开发者常常同时维护多个产品——一个 Web 端管理后台、一个小程序端用户端、一个 API 服务、以及若干个工具脚本。当 AI 能力如 LLM 调用、文本嵌入、向量检索、Prompt 管理需要在多个项目间共享时最常见的反模式是复制粘贴 局部修改把上一个项目的 AI 调用代码复制到新项目修改 API Key 和提示词然后逐渐形成多个版本的发散演化的代码。这种碎片化带来的问题在 34 个项目时开始显现某个 Prompt 模板优化了需要手动同步到 3 个项目的代码API 调用增加了重试和超时逻辑但只有 2 个项目更新了文本嵌入模型升级了新版本需要检查 4 个项目的兼容性。这些维护工作耗费的时间很快超过了当初独立开发、快速上线节省的时间。AI 代码仓库管理的核心思想是将 AI 能力作为独立的共享模块进行治理使其在多项目之间保持单一数据源Single Source of Truth。这不仅仅是一个库的封装更是一种架构纪律——确保 AI 能力的使用方式、Prompt 模板、模型配置和错误处理策略在组织内保持一致。为了实现这一目标架构设计通常分为三个核心层级。最上层是业务项目层包含 Web 管理端、小程序用户端、API 服务及工具脚本等具体应用场景。中间层是 AI 共享能力层采用 Monorepo 模式管理核心组件包括版本化 Prompt 模板库、统一封装的 LLM 客户端、负责向量化与检索的 Embedding 服务以及编排工具调用的 Agent 运行时。最底层是基础设施层提供 LLM API如 OpenAI/Claude、向量数据库如 Qdrant/pgvector以及配置中心用于模型选择与密钥管理。业务项目通过依赖中间层的共享能力模块间接调用底层基础设施从而确保各业务端在 AI 能力使用上的一致性。这种分层结构清晰地界定了职责边界业务层专注于具体场景逻辑共享层负责 AI 能力的标准化封装基础设施层则提供稳定的底层服务支持。接下来我们将重点探讨中间层的设计细节。二、AI 能力共享层的分层设计2.1 Prompt 模板库版本化与可测试Prompt 是 AI 应用中变化最频繁的部分。一个优化得当的 Prompt 能够将 LLM 的响应质量提升 50% 以上但前提是它的修改记录可追溯、效果可评估、变更不影响线上服务。Prompt 模板库的设计遵循以下原则模板文件与代码分离Prompt 模板以单独的.md或.yaml文件存储与业务逻辑解耦。修改 Prompt 不需要重新部署代码。语义化版本每个 Prompt 模板标注版本号v1.2.0支持 A/B 测试时同时使用多个版本。自动化回归测试为 Prompt 编写测试用例——输入一组已知的用户消息验证输出是否包含预期的关键信息。模板变量注入支持{{variable_name}}形式的占位符运行时注入上下文数据。2.2 LLM 客户端统一调用封装在多个项目中直接调用 OpenAI/Claude API 的危险在于重试策略不一致有些 3 次重试、有些没有、超时配置不一致有些 30s、有些无限期、错误处理不一致有些静默忽略、有些打印到控制台。统一的 LLM 客户端负责重试与超时统一的指数退避重试最多 3 次、每次间隔 1s/2s/4s和超时配置默认 60s。降级策略当主模型不可用时如 OpenAI 服务故障自动降级到备用模型如 Claude。请求日志与监控记录每次调用的延迟、Token 消耗和响应状态用于成本核算和性能优化。流式响应封装统一处理 SSE 流式响应各业务端使用相同的回调接口。2.3 Embedding 服务向量化与检索的抽象文本嵌入是推荐、搜索和 RAG 的关键基础设施。Embedding 服务的核心能力包括统一 Embedding 接口提供embed(texts: string[]) → number[][]的标准接口屏蔽底层模型差异。向量数据库适配封装 Qdrant、pgvector、Milvus 等向量数据库的差异通过统一的search/add/delete接口操作。批量处理优化对大批量文本的嵌入生成进行批处理和速率控制避免触发 API 限流。三、Monorepo 结构的生产级实现以下实现展示了基于 Monorepo 的 AI 共享能力仓库的组织方式和核心模块代码packages/ ├── ai-core/ # LLM 客户端核心封装 │ ├── src/ │ │ ├── client.ts # LLM 调用客户端 │ │ ├── types.ts # 类型定义 │ │ ├── retry.ts # 重试策略 │ │ └── streaming.ts # 流式响应处理 │ └── package.json ├── ai-prompts/ # Prompt 模板库 │ ├── prompts/ # 按场景分组的 Prompt 文件 │ │ ├── content/ # 内容生成相关 │ │ ├── analysis/ # 分析诊断相关 │ │ └── recommend/ # 推荐排序相关 │ ├── src/ │ │ ├── loader.ts # Prompt 加载器 │ │ └── version.ts # 版本管理 │ └── __tests__/ # Prompt 回归测试 ├── ai-embeddings/ # Embedding 服务 │ ├── src/ │ │ ├── embedder.ts # 嵌入生成器 │ │ └── vector-store.ts # 向量数据库封装 │ └── package.json └── ai-agent/ # Agent 运行时 ├── src/ │ ├── agent.ts # Agent 执行引擎 │ └── tools.ts # 工具注册与调用 └── package.json// packages/ai-core/src/client.ts /** * 统一 LLM 调用客户端 * 职责抽象多个 AI 服务提供商的差异提供统一接口 */ type Provider openai | anthropic | groq; interface LLMConfig { provider: Provider; model: string; apiKey: string; options?: { temperature?: number; maxTokens?: number; timeout?: number; }; } interface LLMRequest { systemPrompt: string; userMessage: string; // 历史消息上下文 history?: Array{ role: user | assistant; content: string }; } interface LLMResponse { content: string; model: string; usage: { promptTokens: number; completionTokens: number; totalTokens: number; }; latency: number; } interface LLMStreamCallbacks { onToken: (token: string) void; onComplete: (response: LLMResponse) void; onError: (error: Error) void; } class LLMClient { private config: LLMConfig; private retryConfig: { maxRetries: number; baseDelay: number; }; constructor(config: LLMConfig) { this.config config; this.retryConfig { maxRetries: 3, baseDelay: 1000, // 基础延迟 1s }; } /** * 同步调用 LLM一次性请求-响应 */ async call(request: LLMRequest): PromiseLLMResponse { const startTime Date.now(); return this.withRetry(async () { // 根据 provider 路由到对应的 API switch (this.config.provider) { case openai: return this.callOpenAI(request); case anthropic: return this.callAnthropic(request); default: throw new Error(不支持的 Provider: ${this.config.provider}); } }, startTime); } /** * 流式调用 LLM逐 Token 回调 */ async stream(request: LLMRequest, callbacks: LLMStreamCallbacks): Promisevoid { try { const response await this.createStreamRequest(request); const reader response.getReader(); const decoder new TextDecoder(); let fullContent ; const startTime Date.now(); while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value, { stream: true }); const lines chunk.split(\n).filter((line) line.trim() ! ); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); if (data [DONE]) break; try { const parsed JSON.parse(data); const token parsed.choices?.[0]?.delta?.content ?? ; if (token) { fullContent token; callbacks.onToken(token); } } catch { // 忽略解析失败的 chunk } } } } callbacks.onComplete({ content: fullContent, model: this.config.model, usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, latency: Date.now() - startTime, }); } catch (error) { callbacks.onError(error instanceof Error ? error : new Error(String(error))); } } /** * 指数退避重试 */ private async withRetry( fn: () PromiseLLMResponse, startTime: number ): PromiseLLMResponse { let lastError: Error | null null; for (let attempt 0; attempt this.retryConfig.maxRetries; attempt) { try { const response await fn(); response.latency Date.now() - startTime; return response; } catch (error) { lastError error instanceof Error ? error : new Error(String(error)); // 判断是否为可重试的错误网络超时、服务过载 if (!this.isRetryableError(lastError)) { throw lastError; } if (attempt this.retryConfig.maxRetries) { // 指数退避: 1s, 2s, 4s const delay this.retryConfig.baseDelay * Math.pow(2, attempt); console.warn( [LLM] 第 ${attempt 1} 次尝试失败${delay}ms 后重试: ${lastError.message} ); await this.sleep(delay); } } } throw lastError; } /** * 判断错误是否可重试 */ private isRetryableError(error: Error): boolean { const retryableMessages [ timeout, rate limit, server error, service unavailable, ECONNRESET, ETIMEDOUT, ]; return retryableMessages.some((msg) error.message.toLowerCase().includes(msg) ); } /** * 调用 OpenAI API */ private async callOpenAI(request: LLMRequest): PromiseLLMResponse { const messages [ { role: system as const, content: request.systemPrompt }, ...(request.history ?? []).map((h) ({ role: h.role as user | assistant, content: h.content, })), { role: user as const, content: request.userMessage }, ]; const response await fetch(https://api.openai.com/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${this.config.apiKey}, }, signal: AbortSignal.timeout(this.config.options?.timeout ?? 60_000), body: JSON.stringify({ model: this.config.model, messages, temperature: this.config.options?.temperature ?? 0.7, max_tokens: this.config.options?.maxTokens ?? 4096, }), }); if (!response.ok) { const errorBody await response.text(); throw new Error(OpenAI API 错误 (${response.status}): ${errorBody}); } const data await response.json(); const choice data.choices[0]; return { content: choice.message.content, model: data.model, usage: { promptTokens: data.usage.prompt_tokens, completionTokens: data.usage.completion_tokens, totalTokens: data.usage.total_tokens, }, latency: 0, // 由 withRetry 补充 }; } /** * 调用 Anthropic API */ private async callAnthropic(request: LLMRequest): PromiseLLMResponse { const messages [ { role: user as const, content: request.userMessage }, ]; const response await fetch(https://api.anthropic.com/v1/messages, { method: POST, headers: { Content-Type: application/json, x-api-key: this.config.apiKey, anthropic-version: 2023-06-01, }, signal: AbortSignal.timeout(this.config.options?.timeout ?? 60_000), body: JSON.stringify({ model: this.config.model, system: request.systemPrompt, messages, max_tokens: this.config.options?.maxTokens ?? 4096, }), }); if (!response.ok) { const errorBody await response.text(); throw new Error(Anthropic API 错误 (${response.status}): ${errorBody}); } const data await response.json(); return { content: data.content[0].text, model: data.model, usage: { promptTokens: data.usage.input_tokens, completionTokens: data.usage.output_tokens, totalTokens: data.usage.input_tokens data.usage.output_tokens, }, latency: 0, }; } private async createStreamRequest(_request: LLMRequest): PromiseReadableStreamUint8Array { throw new Error(Not implemented); } private sleep(ms: number): Promisevoid { return new Promise((resolve) setTimeout(resolve, ms)); } } // ---- Prompt 模板加载器 ---- interface PromptTemplate { name: string; version: string; system: string; user: string; // 用户消息模板支持 {{}} 占位符 metadata: { description: string; expectedOutputFormat: string; tags: string[]; }; } class PromptRegistry { private prompts: Mapstring, PromptTemplate new Map(); /** * 从 prompts/ 目录加载所有模板 * 在应用启动时执行一次 */ async loadAll(): Promisevoid { // 实际实现中扫描 prompts/ 目录下的 .yaml 文件 const promptFiles await this.discoverPromptFiles(); for (const file of promptFiles) { const template this.parsePromptFile(file); // 以 nameversion 作为唯一键 const key ${template.name}${template.version}; this.prompts.set(key, template); } console.log([Prompts] 已加载 ${this.prompts.size} 个模板); } /** * 按名称和版本获取 Prompt 模板 * param name 模板名称 * param version 版本号默认取最新 */ get(name: string, version?: string): PromptTemplate | undefined { if (version) { return this.prompts.get(${name}${version}); } // 获取预期版本的模板 const entries Array.from(this.prompts.entries()) .filter(([key]) key.startsWith(${name})) .sort() .reverse(); // 降序version 最大的在前 return entries[0]?.[1]; } /** * 渲染 Prompt将变量注入模板 */ render(template: PromptTemplate, variables: Recordstring, string): LLMRequest { let userMessage template.user; let systemPrompt template.system; // 替换模板中的 {{variable}} 占位符 for (const [key, value] of Object.entries(variables)) { const placeholder new RegExp(\\{\\{\\s*${key}\\s*\\}\\}, g); userMessage userMessage.replace(placeholder, value); systemPrompt systemPrompt.replace(placeholder, value); } return { systemPrompt, userMessage }; } private async discoverPromptFiles(): Promisestring[] { return []; // Stub } private parsePromptFile(_content: string): PromptTemplate { return { name: , version: 1.0.0, system: , user: , metadata: { description: , expectedOutputFormat: , tags: [] }, }; } } export { LLMClient, PromptRegistry }; export type { LLMConfig, LLMRequest, LLMResponse, LLMStreamCallbacks, PromptTemplate };四、共享架构的治理权重与兼容性挑战4.1 版本升级的兼容性管理共享模块的一处修改可能影响所有依赖它的项目。版本管理的核心难题是如何在不破坏已有项目的前提下迭代 AI 能力。建议采用双轨制稳定轨以语义化版本发布向前兼容的改动如增加新参数作为 minor 版本不兼容的改动如修改函数签名作为 major 版本。实验轨使用next或canary标签发布实验性功能供早期采用者试用收集反馈后再并入稳定轨。各个业务项目通过package.json中的版本约束决定自己的升级节奏而不是被迫同步升级。4.2 Prompt 的跨项目一致性 vs 项目定制共享 Prompt 模板库的核心价值是一致性但不同项目如管理端和用户端对同一 AI 能力的输出格式和语气要求可能不同。解决思路是将 Prompt 分为核心指令和项目定制两部分核心指令推理逻辑、输出结构、约束条件在共享 Prompt 中定义项目定制语气风格、输出格式、示例偏好通过变量注入或拼接实现。4.3 Monorepo 的构建与部署成本Monorepo 结构带来代码共享便利的同时也引入了构建和部署的复杂度。每个业务项目打包时需要正确引用 workspace 依赖CI/CD 需要做到按变更范围触发构建——仅当 AI 共享模块变更时才触发所有依赖项目的集成测试。Turborepo 或 Nx 等工具可以显著降低这一复杂度。五、总结独立产品的 AI 代码仓库管理本质是将复制粘贴式开发升级为共享模块式治理。通过 Monorepo 结构将 Prompt 模板库、LLM 客户端、Embedding 服务和 Agent 运行时作为独立包进行管理在多个业务项目间形成单一数据源。Prompt 模板的版本化管理确保了优化可追溯、变更可测试统一 LLM 客户端确保了重试、超时、降级策略的一致性Embedding 服务提供了跨项目的向量化基础设施。落地建议从统一 LLM 客户端开始——封装一个跨项目复用的 LLM 调用层接入重试和日志功能替代各项目中散落的直接 API 调用。这一步的工程成本最低约 12 天开发但能立即消除 AI 调用行为的碎片化。然后逐步将 Prompt 模板和 Embedding 服务迁移到共享包中形成完整的 AI 能力共享层。

相关新闻

最新新闻

ngx_output_chain_get_buf

ngx_output_chain_get_buf

1 定义 ngx_output_chain_get_buf 函数 定义在 src/core/ngx_output_chain.cstatic ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) {size_t size;ngx_buf_t *b, *in;ngx_uint_t recycled;in ctx->in->buf;size ctx->buf…

2026/7/20 0:18:02
互联网大厂常见Java面试题及答案汇总(2026持续更新)

互联网大厂常见Java面试题及答案汇总(2026持续更新)

金九银十即将来袭,又是一个跳槽的好季节,准备跳槽的同学都摩拳擦掌准备大面好几场,今天为大家准备了互联网面试必备的 1 到 5 年 Java 面试者都需要掌握的面试题,分别 JVM,并发编程,MySQL,Tomca…

2026/7/20 0:18:02
Copilot数据模型训练数据构成真相(92.3%代码+5.1%自然语言+2.6%隐式意图信号)

Copilot数据模型训练数据构成真相(92.3%代码+5.1%自然语言+2.6%隐式意图信号)

更多请点击: https://kaifayun.com 第一章:Copilot数据模型训练数据构成真相(92.3%代码5.1%自然语言2.6%隐式意图信号) GitHub Copilot 的核心能力并非来自通用大语言模型的简单迁移,而是深度扎根于其训练数据的特异性…

2026/7/20 0:13:02
【AI 2026落地实战白皮书】:全球首批商用大模型API接口清单+性能基准测试数据(仅限Q1释放)

【AI 2026落地实战白皮书】:全球首批商用大模型API接口清单+性能基准测试数据(仅限Q1释放)

更多请点击: https://kaifayun.com 第一章:AI 2026年全球商用大模型落地元年总览 2026年标志着人工智能从实验室验证与行业试点正式迈入规模化商用阶段。全球范围内,超73%的《财富》500强企业已将大模型深度集成至核心业务流,涵盖…

2026/7/20 0:13:02
python数据可视化技巧的100个练习 -- 31. 类别数据的点图

python数据可视化技巧的100个练习 -- 31. 类别数据的点图

重要性★★★☆☆ 难度★★☆☆☆ 你是一家零售公司的数据分析师。你的经理要求你可视化最近产品发布的客户满意度评级分布。评级是分类的,范围从“非常不满意”到“非常满意”。创建一个点图以显示每个评级类别的频率。使用 Python 进行数据处理和可视化。在代码中生成输入…

2026/7/20 0:13:02
智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

在2026世界人工智能大会(WAIC 2026)举办期间,千里科技董事长、阶跃星辰董事长印奇作为特邀嘉宾出席大会开幕式并在大会主论坛(上午场)发表主题演讲《当智能体进入物理世界》。在印奇看来,"智能体"…

2026/7/20 0:13:02

月新闻