Claude Code 生成 svgwrite 户型图,Base URL 填 TaoToken
2026/9/19 3:51:46 网站建设 项目流程

用 Claude Code 配 svgwrite 画户型图,通道这一步其实很快:到 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建一把 Key,再把 Base URL 填成 https://taotoken.net/api,剩下的交给四轮对话。

先说这个是因为,「Claude Code 100 个真实案例」里那个用 AI 生成两室一厅建筑平面图 SVG 的例子,真正劝退人的地方不在 svgwrite 的 API。设计师打开终端,还没敲下pip install svgwrite cairosvg,就已经在登录、切模型、看额度这几件事上耗掉了半小时。而后面那四轮提示词——搭 FloorPlanDrawer 框架、写 design_two_bedroom_apartment()、补 FurnitureLibrary 家具符号库、汇总面积导出 two_bedroom_apartment.svg——每一轮都要把上下文重新发给模型,Token 是一路往上走的。通道没准备好,第一轮就可能断在半路。

这篇按原文的推进顺序走一遍,把「准备通道」和「写绘图代码」拆开讲:TaoToken 只负责给你一把 Key 和一个 Base URL,墙厚 240/120、1:50 比例尺、门弧的 Arc 命令、家具怎么摆,全部还是 svgwrite 的活。

1. 为什么 svgwrite 户型图的卡点在 Claude Code 通道,而不是绘图代码

svgwrite 本身的学习曲线并不陡。它做的事情很朴素:把矩形、直线、路径这类 SVG 基本元素用 Python 对象包一层,再dwg.save()落盘成.svg。真正难的是「让 Claude Code 连续四轮帮你把这套代码写对」,而这件事的成败有两个前提:对话别断、上下文别丢。

1.1 四轮提示词,每一步都在消耗 Token

原文的流程是这样的:第一轮让 Claude Code 搭出 FloorPlanDrawer 类,包含 1:50 的 SCALE、mm_to_px 单位换算,以及墙体、门弧、窗三类图元的绘制方法;第二轮补 design_two_bedroom_apartment(),把 11000×8000 的外轮廓拆成七个房间和内外墙;第三轮加 FurnitureLibrary,画沙发、双人床、L 型橱柜、马桶这些符号;第四轮汇总各房间面积,导出 two_bedroom_apartment.svg。

四轮下来,模型每次都要重新读一遍前面的类定义和房间坐标。对 Claude Code 来说这是一次典型的长上下文任务,中途换账号、换 Key、换模型,都会让已经建立起来的上下文白费。不少人遇到的问题不是代码写错,而是第三轮跑到一半提示额度或鉴权异常,只能从头再来。

1.2 TaoToken 在这件事里只负责 Key 和 Base URL

把 TaoToken 接进来之后,它在整个户型图流程里承担的角色非常单一:提供一把可用的 API Key,和一个统一的 Base URLhttps://taotoken.net/api。它不参与绘图,不参与坐标计算,也不知道 240 墙和 120 隔墙的区别。

换句话说,Claude Code 依然是那个帮你写 Python 的助手,svgwrite 依然是那个真正画图的库,中间只是多了一段稳定的兼容通道。理解了这一点,配置的时候就不会纠结「要不要在 TaoToken 里配户型参数」——不需要。

2. 在 ~/.claude/settings.json 里把 ANTHROPIC_BASE_URL 指向 TaoToken

配置动作本身不到五分钟,但坑都藏在细节里:填哪个文件、末尾要不要/v1、Key 怎么生效。下面按顺序过一遍。

2.1 先去官网创建一把 Key

打开 TaoToken,注册账号后进控制台新建一个 API Key,复制出来先放一边。顺手在模型广场看一眼当前可用的模型 ID,因为ANTHROPIC_MODEL要填的就是那里列出来的名字,而不是凭印象写的字符串。这一步不要和接口地址混:注册、创建 Key、看模型、看用量都在落地页做;真正填进工具的是https://taotoken.net/api,末尾没有/v1

提示:Key 只在创建时完整显示一次,建议先粘到本地临时文件里,配完 settings.json 再删。

2.2 两种等价写法:环境变量与 settings.json

Claude Code 读配置有两条路。临时用,直接在终端里导出环境变量:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="YOUR_API_KEY" export ANTHROPIC_MODEL="<模型广场里列出的模型 ID>"

想长期生效,就写进配置文件。macOS 和 Linux 下是~/.claude/settings.json,Windows 下是%USERPROFILE%\.claude\settings.json

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "<模型广场里列出的模型 ID>" } }

注意键名里的env:这三个变量必须放在env对象内部,写成顶层字段 Claude Code 不会读。

2.3 三个容易填错的位置

第一,ANTHROPIC_BASE_URL不要带/v1。带了之后工具会在后面再拼一次版本号,请求路径变成/api/v1/v1/messages这类四不像的地址,直接 404 或 401。

第二,不要把带查询参数的官网地址填进 Base URL。settings.json里只需要https://taotoken.net/api这个干净地址,UTM 参数是给人点的网页用的,不是给接口用的。

第三,ANTHROPIC_AUTH_TOKEN填的是占位符YOUR_API_KEY对应的真实 Key,不要带引号外的空格,也不要在前面手动加Bearer。工具自己会处理鉴权头。

注意:改完 settings.json 后要重开一个 Claude Code 会话,旧进程不会自动加载新配置。

3. 让 Claude Code 先交 FloorPlanDrawer:1:50 与 mm_to_px

通道通了,第一轮提示词可以发了。这一轮的目标只有一个:拿到一个能跑、能保存、能继续扩展的绘制框架,别急着让它画户型。

3.1 第一轮提示词怎么说

可以用接近原文的说法,但把约束讲清楚:

用 Python 的 svgwrite 写一个 FloorPlanDrawer 类,作为户型图绘制框架。 要求: 1. 图纸比例 1:50,所有对外接口的输入坐标和尺寸都用毫米(mm); 2. 内部提供 mm_to_px(mm) 方法做单位换算; 3. 依次提供 draw_wall(墙体)、draw_door(门弧)、draw_window(窗)三个方法; 4. 用 Drawing 的 group 把墙体、门、窗、家具分层,方便后续单独导出; 5. 先不要写具体户型,只给类定义和一个最小可运行的示例。

把「先不要写具体户型」写进提示词是有意为之。第一轮如果让它同时考虑七个房间,模型很容易把坐标算错,而你还得回头去猜是哪一步错了。

3.2 墙体双线、门弧 A 命令、窗三线

拿到类定义后,先检查三件事。墙体是不是按双线画的(内墙 120、外墙 240,都按厚度的一半向两侧偏移);门弧是不是用了 path 的A命令而不是圆弧近似;窗户是不是三条平行线。

import svgwrite class FloorPlanDrawer: """1:50 住宅平面图绘制框架,输入单位统一为毫米(mm)。""" SCALE = 50 def __init__(self, width_mm, height_mm, filename): self.dwg = svgwrite.Drawing( filename, size=(self.mm_to_px(width_mm), self.mm_to_px(height_mm)), ) self.walls = self.dwg.add(self.dwg.g(id="walls", stroke="black")) self.doors = self.dwg.add(self.dwg.g(id="doors", fill="none", stroke="black")) self.windows = self.dwg.add(self.dwg.g(id="windows", stroke="black")) self.furniture = self.dwg.add(self.dwg.g(id="furniture")) def mm_to_px(self, mm): return mm / self.SCALE def draw_wall(self, x1, y1, x2, y2, thickness=240): half = self.mm_to_px(thickness) / 2 if y1 == y2: for dy in (-half, half): self.walls.add(self.dwg.line( start=(self.mm_to_px(x1), self.mm_to_px(y1) + dy), end=(self.mm_to_px(x2), self.mm_to_px(y2) + dy), stroke_width=2)) else: for dx in (-half, half): self.walls.add(self.dwg.line( start=(self.mm_to_px(x1) + dx, self.mm_to_px(y1)), end=(self.mm_to_px(x2) + dx, self.mm_to_px(y2)), stroke_width=2)) def draw_door(self, x, y, width=900): w = self.mm_to_px(width) px, py = self.mm_to_px(x), self.mm_to_px(y) self.doors.add(self.dwg.path( d=f"M {px} {py} L {px + w} {py} A {w} {w} 0 0 1 {px} {py + w}", stroke_width=2)) def draw_window(self, x, y, length, thickness=240, horizontal=True): t = self.mm_to_px(thickness) for offset in (-t / 2, 0, t / 2): if horizontal: self.windows.add(self.dwg.line( start=(self.mm_to_px(x), self.mm_to_px(y) + offset), end=(self.mm_to_px(x + length), self.mm_to_px(y) + offset), stroke_width=2)) else: self.windows.add(self.dwg.line( start=(self.mm_to_px(x) + offset, self.mm_to_px(y)), end=(self.mm_to_px(x) + offset, self.mm_to_px(y + length)), stroke_width=2))

A命令的参数顺序是rx ry x轴旋转 大弧标志 扫向标志 终点x 终点y。门弧一般是 90 度小弧,所以大弧标志写 0、扫向写 1。这段如果模型写错了,你会在浏览器里看到一根横线或者一个夸张的半圆,很直观。

4. 第二轮补 design_two_bedroom_apartment():11000×8000 怎么拆成七个房间

第二轮最容易翻车。七个房间、内外墙、门洞位置,全靠模型在脑子里做几何划分,一不留神就会出现两个房间重叠、或者外墙长度加起来不是 11000。

4.1 先把房间清单写成表格再让它落代码

比起让它「自己设计一个两室一厅」,更稳的做法是先用文字把分区方案定死,再让它翻译成代码。例如让整个户型按 11000×8000 的外轮廓划分:

房间起点 (x, y)宽×深 (mm)面积
主卧(0, 0)3600×400014.40 m²
次卧(3600, 0)3000×400012.00 m²
卫生间(6600, 0)1800×24004.32 m²
厨房(8400, 0)2600×24006.24 m²
客厅(6600, 2400)4400×560024.64 m²
餐厅(0, 4000)3600×400014.40 m²
书房(3600, 4000)3000×400012.00 m²

这七块正好铺满 11000×8000,加起来 88 平方米,后面做面积汇总时能直接对上。把这张表原样贴给 Claude Code,让它据此写design_two_bedroom_apartment(),比让它自由发挥可靠得多。

4.2 内外墙厚 240 与 120 的处理

户型定义函数里,画墙这一步要显式区分厚度:贴外轮廓的墙传thickness=240,房间之间和卫生间、厨房的隔墙传thickness=120。如果提示词里不写,模型很可能全部按 240 画,图出来之后墙线挤在一起,看着就假。

def design_two_bedroom_apartment(filename="two_bedroom_apartment.svg"): W, H = 11000, 8000 d = FloorPlanDrawer(W, H, filename) # 外墙:240 厚 d.draw_wall(0, 0, W, 0, thickness=240) d.draw_wall(W, 0, W, H, thickness=240) d.draw_wall(W, H, 0, H, thickness=240) d.draw_wall(0, H, 0, 0, thickness=240) # 内墙:120 厚 d.draw_wall(3600, 0, 3600, 4000, thickness=120) d.draw_wall(6600, 0, 6600, 8000, thickness=120) d.draw_wall(8400, 0, 8400, 2400, thickness=120) d.draw_wall(0, 4000, 6600, 4000, thickness=120) d.draw_wall(6600, 2400, W, 2400, thickness=120) return d

墙画完之后再补门弧:主卧、次卧的开在朝向餐厅一侧,卫生间和厨房的门开向客厅方向。draw_door(x, y, width=900)(x, y)是门扇的轴心,轴心位置和门洞位置对齐,弧线才不会压在墙线上。

5. 第三轮 FurnitureLibrary:沙发、双人床、L 型橱柜、马桶

家具这一轮看着轻松,其实是整张图最花时间的地方。原因不在 svgwrite,而在于每个符号都要定义一套自己的坐标基准,模型如果每换一个家具就换一种基准,最后摆放位置会乱套。

5.1 家具符号的坐标基准要统一

提示词里明确要求:所有家具方法的第一个坐标参数都是「家具外接矩形的左下角」,第二、三个参数是宽和深,单位毫米。这样在客厅里摆沙发就是sofa(6800, 2600, 2000, 900)这种一眼能读懂的调用。

class FurnitureLibrary: """家具符号库,坐标基准统一为外接矩形左下角。""" @staticmethod def sofa(dwg, x, y, w=2000, d=900): g = dwg.dwg.g(stroke="black", fill="none", stroke_width=2) g.add(dwg.dwg.rect(insert=(dwg.mm_to_px(x), dwg.mm_to_px(y)), size=(dwg.mm_to_px(w), dwg.mm_to_px(d)))) # 靠背 g.add(dwg.dwg.line( start=(dwg.mm_to_px(x), dwg.mm_to_px(y + d - 150)), end=(dwg.mm_to_px(x + w), dwg.mm_to_px(y + d - 150)))) dwg.furniture.add(g) @staticmethod def double_bed(dwg, x, y, w=1800, d=2000): dwg.furniture.add(dwg.dwg.rect( insert=(dwg.mm_to_px(x), dwg.mm_to_px(y)), size=(dwg.mm_to_px(w), dwg.mm_to_px(d)), stroke="black", fill="none", stroke_width=2)) # 两个枕头 for i in range(2): dwg.furniture.add(dwg.dwg.rect( insert=(dwg.mm_to_px(x + 100 + i * (w / 2)), dwg.mm_to_px(y + d - 500)), size=(dwg.mm_to_px(w / 2 - 200), dwg.mm_to_px(350)), stroke="black", fill="none", stroke_width=1.5)) @staticmethod def l_shaped_counter(dwg, x, y, long_side=2400, short_side=1800, depth=600): pts = [(x, y), (x + long_side, y), (x + long_side, y + depth), (x + depth, y + depth), (x + depth, y + short_side), (x, y + short_side)] dwg.furniture.add(dwg.dwg.polygon( points=[(dwg.mm_to_px(px), dwg.mm_to_px(py)) for px, py in pts], stroke="black", fill="none", stroke_width=2)) @staticmethod def toilet(dwg, x, y): dwg.furniture.add(dwg.dwg.rect( insert=(dwg.mm_to_px(x), dwg.mm_to_px(y)), size=(dwg.mm_to_px(400), dwg.mm_to_px(200)), stroke="black", fill="none", stroke_width=2)) dwg.furniture.add(dwg.dwg.ellipse( center=(dwg.mm_to_px(x + 200), dwg.mm_to_px(y + 500)), r=(dwg.mm_to_px(180), dwg.mm_to_px(280)), stroke="black", fill="none", stroke_width=2))

沙发用矩形加一条靠背线,双人床用矩形加两个枕头,L 型橱柜用polygon走六个点,马桶用水箱矩形加椭圆。四个符号各有各的画法,但坐标基准是统一的,后面摆放就不会互相打架。

5.2 让 Claude Code 自己写一个摆放校验

家具摆完之后,可以让它再补一个小函数:遍历所有家具的包围盒,检查有没有超出所在房间的边界,或者两件家具互相重叠。这个校验不需要精确,粗略的矩形相交判断就够用,但能帮你省掉大量肉眼找问题的时间。

6. 第四轮汇总面积并导出 two_bedroom_apartment.svg

最后一轮把散落的东西收口:算面积、加标注、保存文件。

6.1 面积汇总顺便当一次自检

ROOMS = [ ("主卧", 0, 0, 3600, 4000), ("次卧", 3600, 0, 3000, 4000), ("卫生间", 6600, 0, 1800, 2400), ("厨房", 8400, 0, 2600, 2400), ("客厅", 6600, 2400, 4400, 5600), ("餐厅", 0, 4000, 3600, 4000), ("书房", 3600, 4000, 3000, 4000), ] def summarize_area(rooms): total = 0.0 for name, _, _, w, h in rooms: area = w * h / 1_000_000 total += area print(f"{name}: {area:.2f} m2") print(f"合计: {total:.2f} m2") return total

这个函数顺便是一次几何自检。如果算出来的合计明显小于 88 平方米,说明房间列表里有重叠或者漏了一块;如果大于 88,说明某两块区域叠在一起了。这比盯着 SVG 一点点量要快得多。

6.2 跑 python floor_plan_main.py 看两个信号

入口文件建议叫floor_plan_main.py,里面只做三件事:实例化 FloorPlanDrawer、调用 design_two_bedroom_apartment()、把家具摆上去、dwg.save()

from floor_plan_drawer import FloorPlanDrawer from furniture import FurnitureLibrary if __name__ == "__main__": d = FloorPlanDrawer(11000, 8000, "two_bedroom_apartment.svg") d.draw_wall(0, 0, 11000, 0, thickness=240) # ... 其余墙体与门弧 FurnitureLibrary.double_bed(d, 300, 300) FurnitureLibrary.sofa(d, 6800, 2600) FurnitureLibrary.l_shaped_counter(d, 8400, 300) FurnitureLibrary.toilet(d, 6800, 900) d.dwg.save() print("saved:", "two_bedroom_apartment.svg")

终端里敲:

python floor_plan_main.py

看两个信号就够了。一是看 Claude Code 那边这一轮对话有没有正常返回,中途有没有鉴权类的报错;二是看当前目录下two_bedroom_apartment.svg有没有落盘、文件大小是不是 0 字节。两个都正常,用浏览器打开 SVG,墙体双线、门弧、窗三线应该都在。

7. 401 和 /v1 路径拼接:两个报错怎么回查 settings.json

这套流程里,代码报错通常好定位,难定位的是通道报错。两种最常见。

7.1 401 优先查 Key 有没有真正生效

401 基本可以断定 Key 没被工具读到。排查顺序:先确认settings.json里是env对象内的ANTHROPIC_AUTH_TOKEN,不是写成了别的键名;再确认这个 Key 是从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的控制台里新建并复制完整的,没有多出空格或换行;最后确认改完之后重开了会话。如果用的是一次性的环境变量,注意新开一个终端标签页就失效了。

7.2 请求路径被拼成 /v1 说明 Base URL 写多了

另一种报错长这样:请求地址里出现了/api/v1/v1/messages或者/v1/messages前面重复了一段。原因几乎都是 Base URL 里手滑加了/v1。回到~/.claude/settings.json,把ANTHROPIC_BASE_URL改回https://taotoken.net/api,末尾不要斜杠、不要/v1、不要任何查询参数。

顺带提醒一个容易混淆的点:ModuleNotFoundError: No module named 'svgwrite'这类报错跟通道无关,是 Python 环境没装依赖,回到原文那一步执行pip install svgwrite cairosvg就行。搞清楚报错属于「通道层」还是「代码层」,能省下大量瞎改配置的时间。

8. 导出成功后,去控制台对一下这次调用

two_bedroom_apartment.svg落在目录里,说明通道和代码两头都通了。这时候值得回 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的控制台看一眼用量,确认刚才那四轮提示词是不是都记在了同一个 Key 上——如果只记到一两轮,多半是中途换了 Key 或者会话被意外重开,下一套户型图开始之前最好统一一下。

想先验证模型 ID 和 Base URL 有没有填错,可以在 TaoToken 模型对话 里用同一把 Key 发一条测试消息。这套户型图流程属于典型的长上下文多轮任务,如果打算长期拿 Claude Code 画图,可以先看 Coding Plan 的套餐是否够用;Key 随时在 控制台 API Keys 里补建,环境变量的完整对照表在 Claude Code 接入文档。

真正把这套跑顺之后你会发现,户型图能不能画好,取决于提示词里房间坐标写得够不够死、内外墙厚度有没有区分、家具基准有没有统一;而 Base URL 和 Key 这两样,配一次就不用再动。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询