- 人工智能
- 大模型
- AI Agent
- RAG
- 工具调用
- MCP 服务
- MCP Clients
- 多智能体
【免费下载链接】CangjieMagic
基于仓颉编程语言构建的 LLM Agent DSL,其主要特点包括:声明式 DSL、支持 MCP 协议,支持任务智能规划等。
导读
magic.core.model是 CangjieMagic(仓颉 LLM Agent 框架)的模型抽象层,统一封装了对话(Chat)、向量(Embedding)、文生图(Image)三类模型能力,并通过Model、ChatModel、EmbeddingModel、ImageModel四个接口及配套的请求/响应类型,向上层 Agent DSL、执行器与 RAG 组件提供一致的数据契约。本文基于 docs/package_docs/core.model.md 的 API 参考,结合 src/core/model 下的源码实现与 src/tests/model_test.cj 测试用例,系统讲解每个类型的设计意图、字段语义、同步/异步调用方式与多服务商接入机制,帮助读者在自有项目中正确选用并扩展模型能力。
1. 模型层全景:一个 provider + name 的世界观
在 CangjieMagic 中,任何模型都由两部分标识构成:服务商名称(provider)与模型名称(name)。这一约定定义在Model接口中,位于 src/core/model/model.cj:
public interface Model { prop provider: String prop name: String prop fullName: String { get() { "${provider}:${name}" } } }provider:服务商标识,例如openai、deepseek、dashscope、ollama;name:具体模型名,例如gpt-4o、deepseek-chat;fullName:由接口默认实现拼接出的"provider:name"完整标识,例如deepseek:deepseek-chat。
框架内所有模型配置统一采用<provider>:<model>格式(详见 docs/tutorial.md 的「模型配置」章节)。这一设计使得上层只需传递一个字符串即可完成模型选型,而解析、校验与实例化工作全部交由ModelManager完成。
1.1 三类模型子接口
Model之下按能力划分为三个子接口:
| 接口 | 定义文件 | 能力 | 核心方法 |
|---|---|---|---|
ChatModel | chat_model.cj | 文本对话(含工具调用) | create、asyncCreate |
EmbeddingModel | embedding_model.cj | 文本向量化 | create |
ImageModel | image_model.cj | 文生图 | create |
三者都继承Model,因此天然具备provider/name/fullName属性。源码注释指出未来还可能扩展音频(audio)等模型类型,接口设计为可扩展的开放形态。
2. ChatModel:对话模型的同步与异步双通道
ChatModel是框架中最重要的模型接口,定义于 src/core/model/chat_model.cj:
public interface ChatModel <: Model { prop contextLength: Int64 mut prop maxTokens: Option<Int64> func create(request: ChatRequest): ChatResponse func asyncCreate(request: ChatRequest): AsyncChatResponse }成员语义如下:
| 成员 | 类型 | 说明 |
|---|---|---|
contextLength | Int64 | 模型的上下文长度(用于提示词裁剪、Token 预算等场景) |
maxTokens | mut Option<Int64> | 可动态设置/读取的最大输出 Token 数 |
create(request) | ChatResponse | 同步 API,阻塞等待完整响应 |
asyncCreate(request) | AsyncChatResponse | 异步 API,返回可流式消费的响应对象 |
源码中还保留了被注释掉的便捷重载create(messages: Array<Message>),注释说明因 cjc 0.53.x 存在编译缺陷暂缓加入——这提示开发者:若你的仓颉编译器版本支持重载,可在自定义ChatModel中补充该便捷方法。
2.1 contextLength 与 maxTokens 的默认值
contextLength默认取Config.defaultContextLen(在 src/config/config.cj 中定义为32000),并可通过环境变量CHAT_MODEL_CONTEXT_LEN覆盖,解析逻辑见 src/model/model_manager.cj;maxTokens默认None(表示不限制),可在ChatRequest中按请求级覆盖,也可通过模型的maxTokens属性全局设置,最终取请求级优先。
3. ChatRequest:一次对话请求的完整载体
ChatRequest定义于 src/core/model/chat_request.cj,实现ToString & ToJsonValue,可序列化为 OpenAI 兼容的 JSON 请求体。
3.1 字段一览
| 字段 | 类型 | 说明 |
|---|---|---|
messageList | MessageList | 请求携带的完整消息列表 |
temperature | Option<Float64> | 采样温度,None表示不向服务端下发该字段 |
stop | Option<Array<String>> | 停止词列表 |
tools | Array<Tool> | 关联的工具列表(用于 Function Calling) |
maxTokens | Option<Int64> | 本次请求的最大输出 Token 数 |
3.2 四种构造器(原文档完整继承)
ChatRequest提供了四种重载构造器,覆盖从单字符串到完整消息列表的各类使用场景,全部参数均可用命名参数(!后缀)按需传入:
// 1. 单条用户消息字符串 init(message: String, temperature!: Option<Float64> = None, stop!: Option<Array<String>> = None, tools!: Array<Tool> = [], maxTokens!: Option<Int64> = None) // 2. 单条 Message 对象 init(message: Message, temperature!: Option<Float64> = None, stop!: Option<Array<String>> = None, tools!: Array<Tool> = [], maxTokens!: Option<Int64> = None) // 3. 消息数组 init(messages: Array<Message>, temperature!: Option<Float64> = None, stop!: Option<Array<String>> = None, tools!: Array<Tool> = [], maxTokens!: Option<Int64> = None) // 4. MessageList 对象 init(messageList: MessageList, temperature!: Option<Float64> = None, stop!: Option<Array<String>> = None, tools!: Array<Tool> = [], maxTokens!: Option<Int64> = None)其中「单字符串」构造器内部等价于MessageList([Message.user(message)]),即自动包装为一条 user 角色消息。messageList字段直接引用构造时传入的列表,不做拷贝。
3.3 序列化行为(toJsonValue)
toJsonValue()产出的 JSON 结构(对应 OpenAI/chat/completions请求格式):
messages:始终输出,由每条消息的toJsonValue()映射而来;temperature:仅当为Some时输出;stop:仅当为Some时输出;tools:仅当非空时输出,每个工具序列化为{"type":"function","function":...};max_tokens:仅当为Some时输出。
toString()直接返回toJsonValue().toJsonString()的结果,便于日志打印与调试。
4. ChatResponse:同步响应的统一封装
ChatResponse定义于 src/core/model/chat_response.cj,是一个open类(可被继承,AsyncChatResponse即继承它),同样实现ToString & ToJsonValue。
open public class ChatResponse <: ToString & ToJsonValue { public init( model: String, message: Message, toolRequests!: Array<ToolRequest> = [], usage!: Option<ChatUsage> = None ) prop model: String // 实际响应的模型名 prop message: Message // 助手消息内容 prop toolRequests: Array<ToolRequest> // 模型请求调用的工具列表 prop usage: Option<ChatUsage> // Token 用量统计 }字段语义:
| 属性 | 说明 |
|---|---|
model | 产生该响应的模型标识(服务端返回的 model 字段) |
message | 助手角色消息,包含content与可选的reason(思维链内容) |
toolRequests | 当模型决定调用工具时,以ToolRequest数组返回工具名与参数;为空表示纯文本回复 |
usage | 本请求的 Token 消耗统计,服务端未返回时为None |
toJsonValue()会输出model、message,并条件性输出toolRequests与usage;toString()返回其 JSON 字符串。
4.1 ChatUsage:Token 用量与耗时统计
ChatUsage定义于 src/core/model/chat_usage.cj:
public class ChatUsage <: ToString & ToJsonValue { public let promptTokens: Int64 public let completionTokens: Int64 public let totalTokens: Int64 public let timeCost: Option<Duration> public init(promptTokens!: Int64, completionTokens!: Int64, timeCost!: Option<Duration>) }promptTokens:提示词消耗 Token 数;completionTokens:生成内容消耗 Token 数;totalTokens:构造器内直接计算为promptTokens + completionTokens,不可单独传入;timeCost:请求耗时(Duration),可用于成本与性能观测。
toJsonValue()输出promptTokens/completionTokens/totalTokens,并条件性输出timeCost(JSON 键为timeTost,为源码中已有的拼写,使用时注意);toString()输出形如ChatUsage[completionTokens 100, promptTokens 50, totalTokens 150, timeCost ...]的易读文本。
4.2 同步调用的重试与校验(源码佐证)
在实际使用中,同步调用通常经由 src/model/model_utils.cj 的makeChatImpl完成,它:
- 调用
model.create(request); - 通过
parseChatResponse校验响应:要求消息角色为Assistant,且content、reason、toolRequests至少一项非空,否则视为无效回复; - 无效回复时休眠 500ms 后重试,最多重试
Config.modelRetryNumber(默认 3,见 src/config/config.cj)次。
这一层校验解释了为何上层 Agent 能稳定拿到合法响应——空回复、角色异常等情况不会直接冒泡到业务代码。
5. 异步对话:AsyncChatResponse 与 AsyncChatChunk
流式输出是 LLM 对话的标配能力。ChatModel.asyncCreate返回AsyncChatResponse,定义于 src/core/model/async_chat_response.cj。
5.1 AsyncChatChunk:流式分片
public struct AsyncChatChunk <: ToString { public let message!: Message, // 本片段的增量消息 public let done!: Bool = false, // 仅最后一个分片为 true public let usage!: Option<ChatUsage> = None }message:本分片的增量内容(含可选的reason推理内容);done:标记流结束,仅最后一个分片为true;usage:仅在最后一个分片携带完整用量统计。
toString()会同时输出 usage 与 message 内容。
5.2 AsyncChatResponse:聚合与迭代
AsyncChatResponse <: ChatResponse是同步响应的异步版本,关键成员:
| 成员 | 说明 |
|---|---|
chunks: Iterator<AsyncChatChunk> | 底层分片迭代器,供流式消费 |
iter(withReason!: Bool = true): Iterator<String> | 返回字符串迭代器;withReason=true时优先取推理内容reason,否则取content |
prop message: Message | 同步阻塞获取完整消息:若流未结束,会先消费全部 chunk 再返回聚合结果 |
prop usage: Option<ChatUsage> | 流未结束时返回None,结束后返回携带的用量 |
内部实现要点(async_chat_response.cj):
- 流式消费时逐 chunk 追加
content,并通过AtomicBool标记finished保证线程安全; - 若某个 chunk 的
role与首个 chunk 不一致,抛出ModelException("Inconsistent message role"); - 遇到
done=true的分片时,将聚合文本构造成Message(role, content),记录usage并结束。
5.3 流式测试用例
src/tests/model_test.cj 展示了异步调用的标准用法:
let model = ModelManager.createChatModel(OPENAI_CHAT_MODEL) let msgList = MessageList([Message.user("hello")]) let asyncResp = model.asyncCreate(ChatRequest(msgList)) let content: Array<String> = asyncResp.iter() |> collectArray @Assert(content.size > 0) @Assert(asyncResp.message.content.size > 0)先用iter()消费全部流式分片,再访问message属性确认聚合完整——这正是「先流式、后聚合」的推荐用法。
6. EmbeddingModel:文本向量化
向量模型用于 RAG、语义检索等场景。EmbeddingModel定义于 src/core/model/embedding_model.cj,仅一个方法:
public interface EmbeddingModel <: Model { func create(request: EmbeddingRequest): EmbeddingResponse }6.1 EmbeddingRequest
定义于 src/core/model/embedding_request.cj:
public struct EmbeddingRequest { public let prompt: String public let dimensions: Option<Int64> public init(prompt: String, dimensions!: Option<Int> = None) }prompt:待向量化的输入文本(必填);dimensions:输出向量维度,None时使用服务端默认维度。注意构造器参数类型为Option<Int>,存储字段为Option<Int64>,赋值时会发生隐式类型适配。
6.2 EmbeddingResponse
定义于 src/core/model/embedding_response.cj:
public struct EmbeddingResponse <: ToString { public let data: Array<Float64> public init(data: Array<Float64>) override public func toString(): String // 输出 "Embedding: [...数值...]" }data即最终向量。以 OpenAI 实现为例(src/model/openai/embedding.cj),请求体会带input、model、encoding_format: "float",并在dimensions为Some时附加dimensions字段,响应解析后取data[0].embedding构造EmbeddingResponse。
7. ImageModel:文生图能力
ImageModel定义于 src/core/model/image_model.cj,用于文生图场景(当前已接入 OpenAI、SiliconFlow、智谱 AI,见 model_manager.cj)。
7.1 ImageRequest 与 ImageResponse
image_request.cj:
public struct ImageRequest { public ImageRequest( protected let prompt: String, protected let size!: String = "1024x1024") { } }prompt:图像描述文本(protected字段,外部读取需通过扩展接口);size:输出尺寸,默认"1024x1024"。
image_response.cj:
public struct ImageResponse { public ImageResponse( public let url!: String = "", public let revisedPrompt!: String = "") { } }url:生成图像的访问地址(本地模型可能为空串);revisedPrompt:服务端改写后的提示词,可反映模型对输入的润色结果。
8. ModelException:统一的模型异常
所有模型层的错误统一抛ModelException,定义于 src/core/model/model_exception.cj:
public class ModelException <: Exception { public ModelException(public let error: String) { super(error) } }异常消息场景包括(源码可查):HTTP 请求失败、响应体为空、响应角色不一致、输出被截断(finish_reason == "length")、模型标识非法等(见 src/model/openai/chat.cj、src/model/model_manager.cj)。业务代码可通过catch(ex: ModelException)集中处理模型侧故障。
9. 模型实例化:ModelManager 与多服务商配置
虽然core.model只定义抽象,但实际使用时离不开 src/model/model_manager.cj 提供的ModelManager与ModelConfig。
9.1 ModelConfig 与环境变量约定
ModelConfig(model_manager.cj)是模型构造参数的载体:
public init(provider!: String, kind!: String, name!: String, apiKey!: String = "", baseURL!: String = "", contextLength!: ?Int64 = None)kind取值"chat"/"embedding"/"image";apiKey为空时自动读取<PROVIDER>_API_KEY环境变量;baseURL为空时自动读取<PROVIDER>_BASE_URL环境变量,未设置则使用内置默认值。
内置服务商默认配置表(源码DEFAULT_PROVIDER_MAP,model_manager.cj):
| provider | 默认 Base URL | API Key 环境变量 |
|---|---|---|
| openai | https://api.openai.com/v1 | OPENAI_API_KEY |
| anthropic | https://api.anthropic.com/v1 | ANTHROPIC_API_KEY |
| dashscope | https://dashscope.aliyuncs.com/compatible-mode/v1 | DASHSCOPE_API_KEY |
| ark | https://ark.cn-beijing.volces.com/api/v3 | ARK_API_KEY |
| deepseek | https://api.deepseek.com | DEEPSEEK_API_KEY |
| siliconflow | https://api.siliconflow.cn/v1 | SILICONFLOW_API_KEY |
| zhipuai | https://open.bigmodel.cn/api/paas/v4 | ZHIPUAI_API_KEY |
| maas | 必须由MAAS_BASE_URL提供 | MAAS_API_KEY |
https://generativelanguage.googleapis.com/v1beta/openai | GOOGLE_API_KEY | |
| moonshot | https://api.moonshot.cn/v1 | MOONSHOT_API_KEY |
| openrouter | https://openrouter.ai/api/v1 | OPENROUTER_API_KEY |
| ollama | http://localhost:11434 | 无需 |
| llamacpp | http://localhost:8080 | 无需 |
各服务商的 Chat / Embedding / Image 能力矩阵与示例模型名(如deepseek:deepseek-chat、openai:gpt-4o、dashscope:qwen-plus、ollama:phi-3)详见 docs/tutorial.md 的「模型配置」章节。
9.2 ModelManager 工厂方法
ModelManager提供三组静态工厂方法,并支持注册自定义模型:
// 按 "provider:name" 字符串创建 static func createChatModel(modelName: String): ChatModel static func createEmbeddingModel(modelName: String): EmbeddingModel static func createImageModel(modelName: String): ImageModel // 按 ModelConfig 创建 static func createChatModel(modelConfig: ModelConfig): ChatModel static func createEmbeddingModel(modelConfig: ModelConfig): EmbeddingModel static func createImageModel(modelConfig: ModelConfig): ImageModel // 注册自定义实现(工厂函数) static func registerChatModel(modelName: String, buildFn: () -> ChatModel): Unit static func registerEmbeddingModel(modelName: String, buildFn: () -> EmbeddingModel): Unit static func registerImageModel(modelName: String, buildFn: () -> ImageModel): Unit实现细节:
- 字符串形式先查注册表(
*_BUILD_FN_MAP),命中则直接调用工厂函数; - 未命中则通过
parseModel按:拆分 provider 与 name 并校验服务商合法性(model_manager.cj),llamacpp特殊处理为无需模型名; - 大多数 OpenAI 兼容服务商(dashscope、ark、deepseek、siliconflow、zhipuai、maas、google、moonshot、openrouter)统一复用
OpenAIChatModel,大幅降低接入成本(见 src/model/openai/chat.cj)。
9.3 实际调用示例
以 src/examples/quick_start/main.cj 为参照,最简接入方式是通过 DSL 宏:
import magic.dsl.* import magic.prelude.* import magic.config.Config @agent[model: "deepseek:deepseek-chat"] class BlackCatAssistant { @prompt("你是黑猫警长的助手") } main() { Config.env["DEEPSEEK_API_KEY"] = "<your api key>" let agent = BlackCatAssistant() let result = agent.chat("一只耳来啦") println(result) }底层@agent宏会将model属性解析为ModelManager.createChatModel("deepseek:deepseek-chat")并挂载到agent.model(mut prop model: Option<ChatModel>)。也可以在代码中直接构造模型后调用核心 API:
let model = ModelManager.createChatModel("openai:gpt-4o") let resp = model.create(ChatRequest("你好")) println(resp.message.content) let embed = ModelManager.createEmbeddingModel("openai:text-embedding-ada-002") let vecResp = embed.create(EmbeddingRequest("hello")) println(vecResp.data.size)10. 扩展自定义模型:从接口到注册
core.model的开放设计允许开发者接入任意模型服务商,标准路径是:
- 实现接口:新建类实现
ChatModel/EmbeddingModel/ImageModel(继承Model后须实现provider、name、contextLength、maxTokens及create/asyncCreate),协议转换逻辑可参考OpenAIChatModel的请求组装与流式解析实现; - 注册工厂:调用
ModelManager.registerChatModel("myProvider:myModel", { => MyChatModel(...) }),此后即可用"myProvider:myModel"字符串在@agent[model: ...]与ModelManager.createChatModel中统一使用; - 直接赋值:也可不经注册,直接构造实例后设置
agent.model = MyChatModel(...)(参考 docs/tutorial.md 自定义模型章节)。
11. 与上层组件的协作关系
- Agent:
Agent接口的mut prop model: Option<ChatModel>是每个 Agent 执行对话的入口; - 执行器:
naive/react/tool-loop执行器均通过ChatModel.create获取回复;Config.enableModelToolCall控制是否启用模型原生 Function Calling(见 src/config/config.cj); - RAG:
VectorBuilder接收EmbeddingModel(VectorBuilder(model: model),见 docs/tutorial.md RAG 章节),配合向量数据库完成语义检索; - 对话记忆:
Conversation等组件消费ChatRequest/ChatResponse完成多轮上下文传递。
总结
magic.core.model以「provider + name」的统一模型标识为起点,通过Model及其三个子接口定义了同步/异步对话、向量化、文生图三类能力的数据契约,配合ChatRequest、ChatResponse、ChatUsage、AsyncChatResponse等请求响应类型,以及ModelManager的服务商注册与解析机制,让上层 Agent 代码无需关心各家 API 差异。开发者既可开箱即用地接入 OpenAI 兼容服务、Ollama、Anthropic 等十余家服务商,也可实现并注册自有模型实现,是理解 CangjieMagic 全部上层能力(Agent、RAG、执行器)的基础模块。建议读者进一步阅读 src/core/model 源码与 src/tests/model_test.cj 中的完整测试用例,掌握每种接口的调用细节。
- 人工智能
- 大模型
- AI Agent
- RAG
- 工具调用
- MCP 服务
- MCP Clients
- 多智能体
【免费下载链接】CangjieMagic
基于仓颉编程语言构建的 LLM Agent DSL,其主要特点包括:声明式 DSL、支持 MCP 协议,支持任务智能规划等。
相关推荐
openJiuwen Agent Core 嵌入模型抽象层解析:Embedding 基类与 EmbeddingConfig 配置实战指南
openJiuwen Agent Core 嵌入模型抽象层解析:Embedding 基类与 EmbeddingConfig 配置实战指南 导读 本文聚焦 ope
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习PaddleSpeech 语言模型接口 lm_interface 深入解析:LMInterface 抽象、动态导入与 TransformerLM 实现
PaddleSpeech 语言模型接口 lm_interface 深入解析:LMInterface 抽象、动态导入与 TransformerLM 实现 导读 本
人工智能语音音频Eigent模型抽象层:如何设计统一的AI模型接口和实现
Eigent模型抽象层:如何设计统一的AI模型接口和实现 在当今AI技术快速发展的时代,如何高效地集成和管理多种AI模型成为了开发者的重要挑战。Eigent项目
人工智能AI Agent多智能体大模型本地部署MCP 服务桌面应用工作流自动化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考