
做前端页面、数据可视化大屏或者个人项目的时候我经常要花大量时间在配色上。主题明明已经想好了选出来的颜色却总是差点意思要么整体太艳要么没有层次要么几个颜色放在一起完全不协调。网上虽然有不少配色网站但大部分只能提供通用色板没办法根据“秋日森林”“赛博朋克”这样带情绪的主题去生成一套能直接用于 UI 的完整配色。这篇文章要分享的就是围绕这个需求搭建的一个小工具输入一个主题词自动输出一组包含主色、辅助色、强调色、背景色和文字色的完整配色方案并且可以直接导出成 CSS 变量。核心思路是把色彩科学里的 HSL 色彩模型、色相环和谐规则与豆包大模型驱动的 Agent 能力组合在一起——豆包负责理解主题语义把它翻译成色相、饱和度、明度等可计算参数色彩科学模块负责用确定性的算法精确生成颜色并做对比度校验。如果你正在接触 Agent 开发或者做前端、可视化项目时经常被配色折磨这篇文章应该能给你一个既能落地、又能继续扩展的思路。读完你会掌握H e x / R G B / H S L 的相互转换、常见和谐配色规则、豆包大模型 OpenAI 兼容接口的调用方式以及一个完整的“主题词到配色方案”智能体编排流程。全文代码基于 Python版本细节请以你的实际环境为准。1. 项目背景为什么“输入主题出配色”值得做1.1 配色的真实痛点颜色是设计里最主观、也最难量化的部分。同一个“科技感”有人会想到深蓝有人会想到黑金还有人会想到紫色渐变。对开发者来说更麻烦的是颜色之间还要满足协调关系主色饱和度太高背景就压不住强调色和主色太接近按钮就缺少层级文字色和背景色对比度不够可读性直接崩掉。传统做法是去 Adobe Color、Coolors 这类网站手动选色或者从 Dribbble 上抄一套现成色板。问题在于这类工具默认给你的是五个“看起来还行”的颜色而不是五个“有明确分工”的颜色。真正落到项目里你还需要知道哪个是主色、哪个是强调色、哪个能当背景、哪个能当文字色。这种“主题语义 → 色彩参数 → 最终色板”的链路恰好适合用大模型加算法去自动化。1.2 为什么选择豆包 Agent 而不是纯规则引擎只写一套规则引擎也能处理一部分需求比如“主题里包含‘绿’就把主色相设为 120”。但这种做法非常脆弱换一种说法就失效了。用户说“像雨后的森林”关键词表不一定能识别出“绿色”和“清新”。豆包这类大模型的强项恰恰是语义理解它能从非常口语化的描述里提取出色彩倾向。但反过来如果让大模型直接输出五个十六进制色值效果往往不稳定。模型对“协调”的理解是概率性的同一个主题生成两次结果可能完全不同而且模型输出的 RGB 色值之间很难保证严格的数学关系。所以更合理的架构是豆包 Agent 负责“语义翻译”色彩科学算法负责“精确生成”。大模型给出的是色相、饱和度、明度参数和和谐规则颜色由算法计算出来这样既保留了语义理解能力又保证了配色规则的可复现性。1.3 工具整体架构整个工具的执行流程可以拆成几步用户输入一个主题词例如“赛博朋克”。豆包大模型分析主题输出结构化参数主色相、饱和度、明度、和谐规则。色彩科学模块根据参数在 HSL 色彩空间里按角色生成五色色板。模块对文字色和背景色做 WCAG 对比度校验。命令行输出配色结果也可以导出 CSS 变量文件。用户输入主题 ↓ 豆包 Agent语义分析主色相 / 饱和度 / 明度 / 和谐规则 ↓ 色彩科学模块HSL 转换 和谐配色算法 对比度校验 ↓ 输出配色方案主色 / 辅助色 / 强调色 / 背景色 / 文字色 CSS 变量这里的 Agent 并不是一个很重的框架它由提示词、模型调用、工具函数和异常降级组成。后面我会把每个模块的代码都拆开讲清楚。2. 环境准备与项目结构2.1 运行环境与依赖本文代码使用 Python 编写建议使用 Python 3.9 及以上版本。依赖很简单只有两个openai豆包大模型提供 OpenAI 兼容接口直接复用官方 SDK。python-dotenv读取.env文件中的密钥配置。安装命令pip install openai1.0.0 python-dotenv1.0.0如果你不想用 SDK也可以直接用requests调用 HTTP 接口但用 SDK 可以省去手动拼请求头的麻烦代码更干净。2.2 获取豆包大模型 API 凭证豆包大模型目前通过火山方舟平台对外提供 API 服务接入方式、模型名称和接入点 ID 会随平台迭代变化本文以火山方舟当前的 OpenAI 兼容接口为例具体信息以你控制台里看到的为准。大致步骤如下注册并登录火山方舟控制台。开通大模型服务创建一个 API Key。创建推理接入点拿到类似ep-xxxxxxxx的接入点 ID。把 API Key 和接入点 ID 写入项目根目录的.env文件。需要注意不同账号的接入点 ID 不同而且模型名称不应该直接写在代码里建议通过环境变量读取。API Key 属于敏感信息一定不要提交到 Git 仓库。2.3 创建项目目录建议按照下面的目录结构组织代码把“色彩算法”和“Agent 编排”拆成两个模块后续维护起来会轻松很多palette-agent/ ├── main.py # 命令行入口 ├── agent.py # 豆包 Agent 编排与降级逻辑 ├── color_science.py # 色彩科学核心算法 ├── prompts.py # 提示词统一管理 ├── requirements.txt ├── .env.example # 环境变量示例 └── output/ # 导出文件目录3. 色彩科学基础配色规则从哪来3.1 先理解 RGB、HEX 和 HSL屏幕上所有颜色本质上都是 RGB 三个通道按不同强度混合出来的。HEX 只是 RGB 的十六进制写法比如#4A90E2就是 R74、G144、B226。但 RGB 这种表示方式对“调色”很不友好你很难直接看出#A1D4B4和#83C196哪个更亮、哪个更饱和。HSL 把颜色拆成三个更符合人类直觉的维度色相Hue0 到 359 度的颜色环0 是红色120 是绿色240 是蓝色。饱和度Saturation0 到 1数值越大颜色越鲜艳。明度Lightness0 到 10 是纯黑1 是纯白。色相角度大致颜色0°红60°黄120°绿180°青240°蓝300°品红配色算法的核心思路就是先确定一个基础色相再在色相环上按固定角度偏移配合饱和度与明度的变化生成一组协调的颜色。这也是为什么先讲 HSL因为它特别适合做这类计算。3.2 色相环与常见和谐配色规则色相环上不同角度之间存在几何关系利用这些关系可以生成视觉上协调的配色。常见规则如下规则英文名生成思路单色monochromatic同一色相只改饱和度与明度邻近色analogous主色相邻 30°~60° 的颜色互补色complementary色相环上正好相差 180°分裂互补split-complementary互补色两侧各偏移 30°三角色triadic三个颜色彼此相差 120°本文代码里实现了后四种规则。每种规则本质上就是一组色相偏移角度的列表例如互补色就是围绕主色相偏移 0° 和 180°再辅助一些中间角度来丰富层次。3.3 怎么把主题词翻译成色彩参数这是整个工具最关键的“翻译”环节。人的直觉里“科技感”通常对应冷色调蓝色“秋日”通常对应橙色暖色调“森林”对应绿色。大模型擅长做这种映射但它需要输出结构化数据不能直接抛一段描述文本。设计思路是让模型输出一个 JSON 对象包含四个核心字段base_hue主色相、saturation饱和度、lightness明度、harmony_type和谐规则。后面的 Agent 拿到这些参数后再把它们交给色彩算法模块去精确计算。这样模型只做判断题算法做计算题两端各司其职。4. 核心实现一色彩计算模块4.1 HEX 与 HSL 互转先创建color_science.py实现基础的色彩转换。这部分代码是后面所有逻辑的地基。# -*- coding: utf-8 -*- color_science.py 色彩科学核心模块 1. HEX / RGB / HSL 互转 2. 基于色相环的和谐配色规则 3. WCAG 对比度校验 import re from dataclasses import dataclass from typing import List, Tuple dataclass class Palette: 一套完整的配色方案 theme: str base_color: str harmony_type: str roles: List[str] # 颜色角色与 colors 一一对应 colors: List[str] # HEX 颜色列表 contrast_note: str def hex_to_rgb(hex_color: str) - Tuple[int, int, int]: 将 #RRGGBB 转为 (r, g, b) hex_color hex_color.strip().lstrip(#) if not re.fullmatch(r[0-9a-fA-F]{6}, hex_color): raise ValueError(f非法的 HEX 颜色: {hex_color}) return tuple(int(hex_color[i:i 2], 16) for i in (0, 2, 4)) def rgb_to_hex(rgb: Tuple[int, int, int]) - str: 将 (r, g, b) 转为 #RRGGBB return #{:02X}{:02X}{:02X}.format(*rgb) def rgb_to_hsl(rgb: Tuple[int, int, int]) - Tuple[float, float, float]: RGB - HSLH 范围 [0, 360)S 和 L 范围 [0, 1] r, g, b [x / 255.0 for x in rgb] max_c, min_c max(r, g, b), min(r, g, b) l (max_c min_c) / 2.0 if max_c min_c: h, s 0.0, 0.0 else: d max_c - min_c s d / (2.0 - max_c - min_c) if l 0.5 else d / (max_c min_c) if max_c r: h (g - b) / d (6 if g b else 0) elif max_c g: h (b - r) / d 2 else: h (r - g) / d 4 h / 6.0 return round(h % 1 * 360, 2), round(s, 4), round(l, 4) def hsl_to_rgb(hsl: Tuple[float, float, float]) - Tuple[int, int, int]: HSL - RGB所有参数范围同上 h, s, l hsl h h % 360 c (1 - abs(2 * l - 1)) * s x c * (1 - abs((h / 60.0) % 2 - 1)) m l - c / 2.0 if h 60: r, g, b c, x, 0 elif h 120: r, g, b x, c, 0 elif h 180: r, g, b 0, c, x elif h 240: r, g, b 0, x, c elif h 300: r, g, b x, 0, c else: r, g, b c, 0, x return tuple(round((channel m) * 255) for channel in (r, g, b))转换函数本身不复杂重点在于rgb_to_hsl和hsl_to_rgb是严格的互逆关系。实际项目中可能还要处理带透明度的 RGBA或者短 HEX 缩写#abc这些可以作为扩展点不影响核心逻辑。4.2 生成一套“带角色”的 UI 配色拿到基础 HSL 参数之后就可以按和谐规则生成颜色了。我在这里固定了五个角色主色、辅助色、强调色、背景色、文字色。每个角色有不同的饱和度和明度策略例如背景色必须很浅文字色必须很深这样才能保证页面有层次。HARMONY_RULES { analogous: [0, -30, 30, -45, 45], complementary: [0, 180, 150, 200, 40], triadic: [0, 120, 240, 60, 300], split-complementary: [0, 150, 210, 180, 30], } ROLE_CONFIG [ (主色, 1.00, 0.55), (辅助色, 0.75, 0.68), (强调色, 1.10, 0.42), (背景色, 0.35, 0.92), (文字色, 0.10, 0.22), ] def build_ui_palette(base_hsl: Tuple[float, float, float], harmony_type: str analogous) - Palette: 根据基础色相和和谐规则生成 5 个角色的 UI 配色。 返回顺序主色、辅助色、强调色、背景色、文字色。 rule HARMONY_RULES.get(harmony_type, HARMONY_RULES[analogous]) colors, roles [], [] for idx, (role, s_ratio, lightness) in enumerate(ROLE_CONFIG): hue (base_hsl[0] rule[idx % len(rule)]) % 360 saturation min(1.0, max(0.0, base_hsl[1] * s_ratio)) colors.append(rgb_to_hex(hsl_to_rgb((hue, saturation, lightness)))) roles.append(role) return Palette(theme, base_colorcolors[0], harmony_typeharmony_type, rolesroles, colorscolors)ROLE_CONFIG里的三个数字分别是角色名称、饱和度倍率和明度目标值。比如强调色饱和度倍率是 1.1颜色会更鲜艳背景色明度是 0.92颜色非常浅文字色明度是 0.22颜色接近深灰。色相偏移则由HARMONY_RULES控制不同规则决定了五个颜色在色相环上的分布形态。4.3 WCAG 对比度校验配色好不好看是主观的但文字可读性是可以量化的。WCAG 定义了相对亮度与对比度公式一般要求正文文字与背景的对比度不低于 4.5:1。在代码里加一个校验函数能在生成结果时立刻发现“深色背景配深色文字”这类问题。def relative_luminance(hex_color: str) - float: 计算 WCAG 相对亮度范围 [0, 1] rgb [x / 255.0 for x in hex_to_rgb(hex_color)] linearized [] for channel in rgb: linearized.append( channel / 12.92 if channel 0.03928 else ((channel 0.055) / 1.055) ** 2.4 ) return 0.2126 * linearized[0] 0.7152 * linearized[1] 0.0722 * linearized[2] def contrast_ratio(hex_a: str, hex_b: str) - float: 计算两个颜色之间的对比度WCAG AA 正文要求 4.5 la, lb relative_luminance(hex_a), relative_luminance(hex_b) lighter, darker max(la, lb), min(la, lb) return round((lighter 0.05) / (darker 0.05), 2)contrast_ratio接收两个 HEX 颜色返回对比度数值。如果低于 4.5就可以在输出里给出提示。这个函数也适合用在更广泛的场景比如校验按钮文字和按钮背景色是否可读后面讲 Agent 编排时会把校验结果写到输出里。5. 核心实现二豆包 Agent 智能体5.1 系统提示词设计Agent 的效果很大程度取决于提示词。这里我要求模型扮演资深色彩设计师并且必须输出严格的 JSON。提示词里把每个字段的取值范围、含义都写清楚能显著降低模型乱输出的概率。创建prompts.py# -*- coding: utf-8 -*- 提示词统一管理 SYSTEM_PROMPT 你是一位资深色彩设计师负责把用户的主题描述翻译成可计算的色彩参数。 请严格按 JSON 输出不要输出任何多余文字。JSON 结构如下 { base_hue: 210, saturation: 0.75, lightness: 0.55, harmony_type: analogous, mood_words: [冷静, 科技感], reason: 简短说明为什么选择这些参数 } 字段说明 - base_hue: 主色相取值 0 到 359 - saturation: 主饱和度取值 0 到 1 - lightness: 主明度取值 0 到 1 - harmony_type: analogous(邻近色)/complementary(互补色)/triadic(三角色)/split-complementary(分裂互补) def build_user_prompt(theme: str) - str: return f请为「{theme}」设计一套配色参数。提示词里写清楚 JSON 结构等于给模型一个输出契约。实际使用中如果模型偶尔还是会输出 Markdown 代码块或额外解释文字就需要在后面加一层解析兜底。5.2 让模型输出结构化 JSON豆包大模型提供 OpenAI 兼容接口所以可以直接用openai客户端调用。部分接口支持response_format{type: json_object}如果当前接入点支持建议打开如果不行就用下面的解析函数做兼容。# -*- coding: utf-8 -*- agent.py 豆包 Agent 编排主题 - 语义分析 - 调色彩工具 - 返回配色 import json import os import re from typing import Optional from dotenv import load_dotenv from openai import OpenAI from color_science import ( Palette, build_ui_palette, contrast_ratio, ) from prompts import SYSTEM_PROMPT, build_user_prompt load_dotenv() def _extract_json(text: str) - dict: 从模型输出中提取 JSON兼容 markdown 代码块包裹和前后噪声 text text.strip() text re.sub(r^(?:json)?\s*|\s*$, , text, flagsre.MULTILINE) try: return json.loads(text) except json.JSONDecodeError: match re.search(r\{.*\}, text, re.S) if match: return json.loads(match.group(0)) raise ValueError(无法从模型输出中解析 JSON) def _validate_params(params: dict) - dict: 对模型输出的色彩参数做边界校验防止生成非法颜色 params[base_hue] int(params.get(base_hue, 0)) % 360 params[saturation] min(1.0, max(0.0, float(params.get(saturation, 0.7)))) params[lightness] min(1.0, max(0.0, float(params.get(lightness, 0.55)))) if params.get(harmony_type) not in ( analogous, complementary, triadic, split-complementary ): params[harmony_type] analogous return params_extract_json处理的是模型输出不干净的情况_validate_params处理的是参数越界的情况。这两层防御非常必要因为模型输出本质上不可控所有外部输入都要先经过校验再进入算法模块。5.3 Agent 编排与工具函数真正的 Agent 核心是一个编排类PaletteAgent。它负责调用模型、解析参数、调用色彩工具、对比度校验以及在异常时降级。def local_rule_fallback(theme: str) - dict: 无 API / 调用失败时用关键词表做兜底保证工具仍然可用 rules [ ((森林, 自然, 绿, 环保, 春天), (120, 0.60, 0.50), analogous), ((科技, 赛博, 蓝, 未来, 极客), (210, 0.75, 0.55), complementary), ((秋天, 橙, 暖, 复古, 日落), (30, 0.70, 0.55), split-complementary), ((浪漫, 粉, 少女, 甜), (340, 0.55, 0.65), analogous), ((极简, 黑白, 性冷淡, 高级感), (0, 0.05, 0.50), analogous), ] for keywords, hsl, harmony in rules: if any(k in theme for k in keywords): return { base_hue: hsl[0], saturation: hsl[1], lightness: hsl[2], harmony_type: harmony, mood_words: [], reason: 本地关键词兜底规则, } return { base_hue: 200, saturation: 0.6, lightness: 0.55, harmony_type: analogous, mood_words: [], reason: 默认规则, } class PaletteAgent: 配色智能体负责任务编排、模型调用、工具执行和异常降级 def __init__(self, api_key: Optional[str] None, endpoint_id: Optional[str] None): self.api_key api_key or os.getenv(DOUBAO_API_KEY) self.endpoint_id endpoint_id or os.getenv(DOUBAO_ENDPOINT_ID) self.client None if self.api_key and self.endpoint_id: # 豆包大模型提供 OpenAI 兼容接口base_url 指向火山方舟 self.client OpenAI( api_keyself.api_key, base_urlhttps://ark.cn-beijing.volces.com/api/v3, ) def analyze_theme(self, theme: str) - dict: 第一步让豆包把主题翻译成色彩参数 if self.client is None: return local_rule_fallback(theme) try: response self.client.chat.completions.create( modelself.endpoint_id, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: build_user_prompt(theme)}, ], temperature0.3, # 若当前接口支持可以开启 # response_format{type: json_object}, ) content response.choices[0].message.content params _extract_json(content) return _validate_params(params) except Exception as exc: print(f[Agent] 模型调用失败使用本地规则兜底{exc}) return local_rule_fallback(theme) def generate_palette(self, theme: str) - Palette: 第二步调用色彩科学模块生成最终配色 params self.analyze_theme(theme) base_hsl (params[base_hue], params[saturation], params[lightness]) palette build_ui_palette(base_hsl, params.get(harmony_type, analogous)) palette.theme theme ratio contrast_ratio(palette.colors[3], palette.colors[4]) palette.contrast_note ( f文字/背景对比度 {ratio}:1WCAG AA 要求 4.5:1 if ratio 4.5 else f文字/背景对比度 {ratio}:1偏低建议手动加深文字色 ) return palette def render_palette(palette: Palette) - str: 把配色方案渲染成可读文本 lines [ f主题{palette.theme}, f主色{palette.base_color} 规则{palette.harmony_type}, , ] for role, color in zip(palette.roles, palette.colors): lines.append(f {role:4} {color}) lines.append() lines.append(palette.contrast_note) return \n.join(lines)generate_palette是整个 Agent 的主流程先分析主题再调用色彩算法最后做对比度校验。所有可能失败的环节都被analyze_theme里的异常捕获包住即便豆包接口暂时不可用工具也会用本地关键词表兜底输出不会直接崩溃。5.4 本地规则兜底与 Function Calling 扩展local_rule_fallback不是一个临时补丁而是一个重要的可用性设计。实际项目里API 的限流、超时、密钥过期都可能发生有了兜底规则工具至少能保持“可用”状态。如果你想把项目往更完整的 Agent 方向扩展官方推荐的做法是 Function Calling 模式。也就是把色彩算法模块注册成工具函数让模型自己决定何时调用。下面是一个工具 schema 示例tools [ { type: function, function: { name: build_ui_palette, description: 根据色相、饱和度、明度和和谐规则生成 UI 配色, parameters: { type: object, properties: { base_hue: {type: number, minimum: 0, maximum: 359}, saturation: {type: number, minimum: 0, maximum: 1}, lightness: {type: number, minimum: 0, maximum: 1}, harmony_type: { type: string, enum: [analogous, complementary, triadic, split-complementary], }, }, required: [base_hue, saturation, lightness, harmony_type], }, }, } ]在这个模式下模型返回tool_calls程序执行对应的 Python 函数再把结果传回模型做下一步判断。本文为了保持代码简短采用了“结构化输出 固定调用”的简化方案但两种思路的核心边界是一致的模型不直接算颜色只负责出参数。6. 完整实战运行演示与结果说明6.1 配置环境变量先创建.env文件内容格式参考.env.examplecp .env.example .env打开.env填入真实信息DOUBAO_API_KEY你的火山方舟API Key DOUBAO_ENDPOINT_IDep-你的推理接入点ID然后安装依赖pip install -r requirements.txt6.2 运行命令行工具创建命令行入口main.py# -*- coding: utf-8 -*- main.py 命令行入口python main.py 赛博朋克 import argparse import os import sys from agent import PaletteAgent, render_palette def export_css(palette, filepathoutput/palette.css) - str: 把配色导出为 CSS 变量方便直接用于前端项目 template :root {{ --color-primary: {colors[0]}; --color-secondary: {colors[1]}; --color-accent: {colors[2]}; --color-bg: {colors[3]}; --color-text: {colors[4]}; }} os.makedirs(os.path.dirname(filepath), exist_okTrue) with open(filepath, w, encodingutf-8) as f: f.write(template.format(colorspalette.colors)) return filepath def main(): parser argparse.ArgumentParser(description豆包 Agent 自动配色工具) parser.add_argument(theme, help主题描述例如赛博朋克) parser.add_argument(--export, actionstore_true, help同时导出 CSS 变量文件) parser.add_argument(--endpoint, defaultNone, help推理接入点 ID默认读环境变量) args parser.parse_args() agent PaletteAgent(endpoint_idargs.endpoint) palette agent.generate_palette(args.theme) print(render_palette(palette)) if args.export: path export_css(palette) print(f\nCSS 变量已导出{path}) if __name__ __main__: sys.exit(main())执行python main.py 赛博朋克 --export示例输出如下不同模型返回的参数会有差异主题赛博朋克 主色#3872E8 规则complementary 主色 #3872E8 辅助色 #6B9BE8 强调色 #E838C2 背景色 #DCE6F5 文字色 #2A2F38 文字/背景对比度 10.2:1WCAG AA 要求 4.5:1 CSS 变量已导出output/palette.css如果不配置 API Key工具会自动走本地规则兜底。比如执行python main.py 秋日森林会匹配到橙色暖色调关键词输出一套橙棕色系配色。这对调试和演示非常方便。6.3 导出 CSS 变量使用--export参数后output/palette.css里的内容大致如下:root { --color-primary: #3872E8; --color-secondary: #6B9BE8; --color-accent: #E838C2; --color-bg: #DCE6F5; --color-text: #2A2F38; }把这段代码复制到前端项目的全局样式里就可以直接使用var(--color-primary)这类变量。你也可以在export_css基础上扩展输出 Tailwind CSS 的tailwind.config.js色板配置或者生成设计稿工具用的 ASE 色板文件。6.4 输出效果说明回来看这套输出主色是比较正的电光蓝辅助色是偏浅的蓝强调色变成了带霓虹感的品红背景色是极浅的蓝白文字色是深灰。整体既有科技感的冷色调又有霓虹撞色层次也够。这就是 Agent 负责语义、算法负责精确计算带来的效果。如果你想调整风格可以改color_science.py里的ROLE_CONFIG例如把背景色明度从 0.92 改成 0.80配色整体会更深沉或者把强调色饱和度倍率从 1.1 改成 0.8颜色会更温和。参数都集中在常量表里调起来非常方便。7. 常见问题与排查清单7.1 鉴权失败与网络超时如果看到AuthenticationError、401 之类的错误优先检查.env里的DOUBAO_API_KEY是否配置正确以及火山方舟账号是否已经开通对应服务。另一个常见问题是接入点 ID 写成了模型名称两者在火山方舟体系里是不同概念接入点 ID 通常是ep-开头。网络超时一般表现为APIConnectionError。先确认base_url是否正确再确认网络环境能否访问目标域名。生产环境建议设置超时时间并增加重试逻辑比如用tenacity库做指数退避重试。7.2 模型输出解析失败模型偶尔会不按提示词输出比如在 JSON 前后加了解释文字或者把字段名改成了单引号。_extract_json已经做了兼容但如果仍然解析失败可以尝试两个方向一是在系统提示词里增加 few-shot 示例把“正确的输入输出对”直接写进提示词二是开启response_format{type: json_object}强制模型输出 JSON。7.3 Agent 执行中途被终止在部分 Agent 框架里你可能会看到类似agent execution terminated due to error.的提示。这通常不是某个固定的错误码而是运行器在某个环节抛出未捕获异常后的兜底提示。常见原因包括上下文过长、工具函数入参不合法、返回值超过长度限制。排查思路是先看完整堆栈定位是模型调用抛错还是工具函数抛错。然后在每个环节加异常捕获和参数校验最后再加一层全局兜底例如本文的local_rule_fallback。Agent 项目里兜底策略不是可选项而是必选项。7.4 生成结果“不好看”颜色好不好看确实有主观成分但更多时候是参数范围问题。如果整体太艳可以把ROLE_CONFIG里的饱和度倍率整体调低如果背景和主色太接近可以调高背景色明度如果强调色不明显可以把强调色的饱和度倍率调高。建议把调整过的优秀结果记录下来形成自己的“色彩经验库”再把这些经验补充到提示词或参数配置里。问题现象常见原因解决思路返回 401 鉴权失败API Key 无效或未配置核对.env确认账号已开通服务连接超时 / APIConnectionErrorbase_url 或接入点 ID 配置错误检查配置增加超时与重试模型输出无法解析 JSON提示词约束不足开启 json_object 模式使用_extract_jsonAgent 执行中途被终止上下文过长、参数越界、异常未捕获限制历史消息校验参数异常兜底每次生成结果差异大temperature 偏高调到 0.2~0.4对同一主题做缓存配色“不好看”饱和度/明度范围不匹配调整ROLE_CONFIG参数沉淀经验库8. 最佳实践与扩展方向8.1 大模型与算法各自的职责边界这个项目最核心的设计决策是让大模型只负责“语义到参数”的翻译所有具体色值都由确定性算法计算。这样做有几点好处结果可复现、计算成本低、逻辑可解释、调试方便。如果反过来让模型直接生成色值你很难回答“为什么这次是蓝色下次是绿色”的问题。这个原则也适用于其他 Agent 工具。凡是涉及精确计算的逻辑比如金额换算、坐标计算、正则匹配都应该交给代码而不是交给大模型。大模型的优势是理解和规划不是精确计算。8.2 提示词稳定性和成本控制提示词需要像代码一样管理。SYSTEM_PROMPT里的 JSON 结构、字段说明、few-shot 示例每次都固定不变用户输入才拼接进去。这样既方便测试也方便后续迭代。温度建议设置在 0.2 到 0.4 之间太低会重复太高会漂移。另外相同主题重复调用 API 很浪费。可以按主题的哈希缓存结果例如把 “赛博朋克” 生成的参数存到本地 JSON 或 SQLite命中缓存就直接跳过模型调用。流量大了之后还可以把缓存升级为 Redis并设置合理的过期时间。8.3 密钥、校验与安全边界API Key 是敏感信息必须放在.env并且加入.gitignore绝不能提交到仓库。代码里对模型输出的所有参数都要做边界校验例如饱和度必须落在 0 到 1 之间色相必须落在 0 到 359 之间和谐规则必须属于白名单。更重要的安全边界是不要让模型直接拼接并执行任意命令行或代码。本文的 Agent 只会把模型输出转换成一组有限的色彩参数不会执行模型给出的任何逻辑风险面很小。如果你的 Agent 需要执行代码一定要在沙箱环境里运行并遵循最小权限原则。8.4 可以继续扩展的方向这个工具只是一个起点可以扩展的方向其实很多。例如让 Agent 掌握export_css工具实现“输入主题 → 自动生成页面样式”的完整链路增加色盲安全配色校验在生成时避开红绿无法区分的组合或者接入设计稿生成工具把配色方案直接渲染成预览图。如果你想深入学习 Agent 开发建议按这个顺序推进先掌握 Function Calling让模型学会调用外部工具再研究 Agent 的记忆能力让工具记住用户偏好的色系最后引入规划能力让 Agent 自动拆解“配色 → 预览 → 反馈调整”这类多步任务。如果只想从本项目里带走一条经验我会建议记住这句话让大模型负责语义理解让算法负责精确计算。配色如此其他涉及确定性计算的 Agent 工具也大多如此。