Clojure安全LLM输出工具 是 AI Skill Hub 本期精选AI工具之一。综合评分 7.5 分,整体质量较高。我们推荐使用将其纳入你的 AI 工具库,帮助提升工作效率。
Type‑safe LLM output for Clojure,支持任何文本模型,提高开发效率。
Clojure安全LLM输出工具 是一款基于 Clojure 开发的开源工具,专注于 Clojure、AI、LLM 等核心功能。作为 GitHub 开源项目,它拥有活跃的社区支持和持续的版本迭代,代码完全透明可审计,支持本地部署以保护数据隐私。无论是个人使用还是集成到企业工作流,都能提供稳定可靠的解决方案。
Type‑safe LLM output for Clojure,支持任何文本模型,提高开发效率。
Clojure安全LLM输出工具 是一款基于 Clojure 开发的开源工具,专注于 Clojure、AI、LLM 等核心功能。作为 GitHub 开源项目,它拥有活跃的社区支持和持续的版本迭代,代码完全透明可审计,支持本地部署以保护数据隐私。无论是个人使用还是集成到企业工作流,都能提供稳定可靠的解决方案。
# 克隆仓库 git clone https://github.com/Blockether/svar cd svar # 查看安装说明 cat README.md # 按 README 完成环境依赖安装后即可使用
# 查看帮助 svar --help # 基本运行 svar [options] <input> # 详细使用说明请查阅文档 # https://github.com/Blockether/svar
# svar 配置说明 # 查看配置选项 svar --config-example > config.yml # 常见配置项 # output_dir: ./output # log_level: info # workers: 4 # 环境变量(覆盖配置文件) export SVAR_CONFIG="/path/to/config.yml"

(comment
;; Single provider
(def router
(svar/make-router [{:id :openai
:api-key (System/getenv "OPENAI_API_KEY")
:models [{:name "gpt-4o"}
{:name "gpt-4o-mini"}]}])))
lazytest/skip=true
;; deps.edn
{:deps {com.blockether/svar {:mvn/version "0.7.11"}}}
(require '[com.blockether.svar.core :as svar])
;; Create a router — the single entry point for all LLM calls.
;; Every function takes the router as its first argument.
(comment
(def router (svar/make-router [{:id :openai
:api-key (System/getenv "OPENAI_API_KEY")
:models [{:name "gpt-4o"}]}])))
Every ask! call accepts :routing to control provider/model selection:
(comment
;; Let the router pick the cheapest model
(svar/ask! router {:spec my-spec
:messages [(svar/user "...")]
:routing {:optimize :cost}})
;; Or the most capable
(svar/ask! router {:spec my-spec
:messages [(svar/user "...")]
:routing {:optimize :intelligence}})
;; Pin to a specific provider + model
(svar/ask! router {:spec my-spec
:messages [(svar/user "...")]
:routing {:provider :openai :model "gpt-4o-mini"}}))
svar now uses explicit transport names for OpenAI-compatible providers:
:openai-compatible-chat → /chat/completions:openai-compatible-responses → /responses:anthropic → /messagesKnown provider profiles choose the right transport for you. For custom providers, set :api-style explicitly:
(comment
(def router
(svar/make-router
[{:id :my-openai-gateway
:api-key (System/getenv "MY_GATEWAY_API_KEY")
:base-url "https://gateway.example.com/v1"
:api-style :openai-compatible-chat
:models [{:name "gpt-4o"}]}
{:id :my-responses-gateway
:api-key (System/getenv "MY_RESPONSES_API_KEY")
:base-url "https://gateway.example.com/v1"
:api-style :openai-compatible-responses
:models [{:name "gpt-5.5"}]}])))
The spec DSL defines the shape of LLM output. Every field has a name, type, cardinality, and description.
| Constant | Type |
|---|---|
TYPE_STRING | String |
TYPE_INT | Integer |
TYPE_FLOAT | Float |
TYPE_BOOL | Boolean |
TYPE_DATE | ISO date (YYYY-MM-DD) |
TYPE_DATETIME | ISO datetime |
TYPE_KEYWORD | Clojure keyword (rendered as string, keywordized on parse) |
TYPE_REF | Reference to another spec |
TYPE_INT_V_1 … TYPE_INT_V_12 | Fixed-size integer vectors (1–12 elements) |
TYPE_STRING_V_1 … TYPE_STRING_V_12 | Fixed-size string vectors |
TYPE_DOUBLE_V_1 … TYPE_DOUBLE_V_12 | Fixed-size double vectors |
TYPE_KEYWORD)String values automatically become Clojure keywords on parse — useful for status codes, categories, and enum-like fields that you want as keywords in your code:
(def status-spec
(svar/spec
(svar/field svar/NAME :status
svar/TYPE svar/TYPE_KEYWORD
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Current status")))
;; String "active" in JSON becomes keyword :active in Clojure
(svar/str->data-with-spec "{\"status\": \"active\"}" status-spec)
;; => {:status :active}
VALUES)When a field should only contain one of a fixed set of values — status codes, categories, severity levels — use VALUES with a map of {value description}. The descriptions are included in the LLM prompt so it understands what each value means, which dramatically improves output accuracy:
(def sentiment-spec
(svar/spec
(svar/field svar/NAME :sentiment
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Sentiment classification"
svar/VALUES {"positive" "Favorable or optimistic tone"
"negative" "Unfavorable or critical tone"
"neutral" "Balanced or factual tone"})
(svar/field svar/NAME :confidence
svar/TYPE svar/TYPE_FLOAT
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Confidence score from 0.0 to 1.0")))
;; Valid enum value passes
(svar/validate-data sentiment-spec {:sentiment "positive" :confidence 0.95})
;; => {:valid? true}
;; Invalid enum value caught
(:valid? (svar/validate-data sentiment-spec {:sentiment "happy" :confidence 0.8}))
;; => false
REQUIRED)Fields are required by default — the LLM must provide a value. Set REQUIRED false when a field might legitimately be absent (e.g., a phone number the source text doesn't mention). Optional fields parse as nil when missing, and validation passes without them:
(def contact-spec
(svar/spec
(svar/field svar/NAME :name
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Full name")
(svar/field svar/NAME :phone
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/REQUIRED false
svar/DESCRIPTION "Phone number if available")))
;; Validation passes without optional fields
(svar/validate-data contact-spec {:name "Jane Doe"})
;; => {:valid? true}
;; But fails without required fields
(svar/validate-data contact-spec {:phone "555-1234"})
;; => {:valid? false, :errors [{:error :missing-required-field, :field :name, :path "name"}]}
CARDINALITY_MANY)When a field holds multiple values — tags, authors, line items — use CARDINALITY_MANY. The LLM returns a JSON array, parsed as a Clojure vector:
(def article-spec
(svar/spec
(svar/field svar/NAME :title
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Article title")
(svar/field svar/NAME :tags
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_MANY
svar/DESCRIPTION "List of tags")))
;; Arrays are parsed as Clojure vectors
(svar/str->data-with-spec "{\"title\": \"SVAR Guide\", \"tags\": [\"clojure\", \"llm\", \"parsing\"]}" article-spec)
;; => {:title "SVAR Guide", :tags ["clojure" "llm" "parsing"]}
(svar/validate-data article-spec {:title "SVAR Guide" :tags ["clojure" "llm"]})
;; => {:valid? true}
TYPE_REF / TARGET)When your LLM output has nested objects — a company with an address, an order with line items — you define each sub-object as its own named spec, then reference it with TYPE_REF + TARGET. This keeps specs composable and reusable: define Address once, reference it from Company, Person, Order, etc.
Pass referenced specs via {:refs [address-spec]} so the prompt generator and parser know how to handle them. Combine with CARDINALITY_MANY for arrays of nested objects (e.g., branch offices).
(def address-spec
(svar/spec :Address
(svar/field svar/NAME :street
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Street address")
(svar/field svar/NAME :city
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "City name")))
(def company-spec
(svar/spec
{:refs [address-spec]}
(svar/field svar/NAME :name
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Company name")
(svar/field svar/NAME :headquarters
svar/TYPE svar/TYPE_REF
svar/CARDINALITY svar/CARDINALITY_ONE
svar/TARGET :Address
svar/DESCRIPTION "HQ address")
(svar/field svar/NAME :branches
svar/TYPE svar/TYPE_REF
svar/CARDINALITY svar/CARDINALITY_MANY
svar/TARGET :Address
svar/DESCRIPTION "Branch office addresses")))
;; Parse nested JSON — refs become nested maps/vectors automatically
(svar/str->data-with-spec
"{\"name\": \"Acme Corp\", \"headquarters\": {\"street\": \"123 Main St\", \"city\": \"SF\"}, \"branches\": [{\"street\": \"456 Oak Ave\", \"city\": \"LA\"}]}"
company-spec)
;; => {:name "Acme Corp", :headquarters {:street "123 Main St", :city "SF"}, :branches [{:street "456 Oak Ave", :city "LA"}]}
;; Ref registry maps spec names to their definitions
(vec (keys (svar/build-ref-registry company-spec)))
;; => [:Address]
KEY-NS)When LLM output maps directly to Datomic/Datalevin entities, you want namespaced keys (:page.node/type instead of :type). KEY-NS adds a namespace prefix to all keys during parsing, so you can transact LLM results straight into your database without manual key transformation.
This is especially useful when multiple specs share field names like :type or :id — namespacing disambiguates them.
(def node-spec
(svar/spec :node
{svar/KEY-NS "page.node"}
(svar/field svar/NAME :type
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Node type")
(svar/field svar/NAME :content
svar/TYPE svar/TYPE_STRING
svar/CARDINALITY svar/CARDINALITY_ONE
svar/DESCRIPTION "Text content")))
;; Keys are automatically namespaced — ready for d/transact!
(svar/str->data-with-spec "{\"type\": \"heading\", \"content\": \"Introduction\"}" node-spec)
;; => {:page.node/type "heading", :page.node/content "Introduction"}
该工具提供了安全的LLM输出功能,支持Clojure开发,值得尝试,但需要进一步优化和完善。
AI Skill Hub 为第三方内容聚合平台,本页面信息基于公开数据整理,不对工具功能和质量作任何法律背书。
建议在沙箱或测试环境中充分验证后,再部署至生产环境,并做好必要的安全评估。
✅ Apache 2.0 — 宽松开源协议,可商用,需保留版权声明和 NOTICE 文件,含专利授权条款。
经综合评估,Clojure安全LLM输出工具 在AI工具赛道中表现稳健,质量良好。如果你已有明确的使用需求,可以直接上手体验;如果还在评估阶段,建议对比同类工具后再做决策。
| 原始名称 | svar |
| 原始描述 | 开源AI工具:Type‑safe LLM output for Clojure. Works with any text‑only model.。⭐13 · Clojure |
| Topics | ClojureAILLMstructured-output |
| GitHub | https://github.com/Blockether/svar |
| License | Apache-2.0 |
| 语言 | Clojure |
收录时间:2026-06-07 · 更新时间:2026-06-07 · License:Apache-2.0 · AI Skill Hub 不对第三方内容的准确性作法律背书。