做视频网站免费观看爱代理ip国外软件

张小明 2025/12/31 18:45:50
做视频网站免费观看爱,代理ip国外软件,asp源码下载,wordpress系统安装AI 后端开发 第 1 篇 | 预估阅读#xff1a;12 分钟4 个星期#xff0c;4 个 LLM#xff0c;47 次代码修改 小禾以为后端架构搞定了#xff0c;可以安心写业务了。 直到老板开始关心技术选型。 第一周#xff1a; 老板#xff1a;“我们要用最好的#xff…AI 后端开发 · 第 1 篇 | 预估阅读12 分钟4 个星期4 个 LLM47 次代码修改小禾以为后端架构搞定了可以安心写业务了。直到老板开始关心技术选型。第一周老板“我们要用最好的上 GPT-5.1”小禾屁颠屁颠地接入了 OpenAIfromopenaiimportOpenAI clientOpenAI(api_keysk-xxx)defgenerate_story(prompt):responseclient.chat.completions.create(modelgpt-5.1,messages[{role:user,content:prompt}])returnresponse.choices[0].message.content效果确实好账单也确实好看——一个月烧了两万块。第二周老板看了账单“换 Gemini 3.0 吧Google 有免费额度。”小禾开始改代码importgoogle.generativeaiasgenai genai.configure(api_keyxxx)modelgenai.GenerativeModel(gemini-3.0-pro)defgenerate_story(prompt):responsemodel.generate_content(prompt)returnresponse.textAPI 完全不一样消息格式不一样响应结构也不一样。小禾改了两天代码。第三周老板“数据安全很重要我们用本地的 Ollama跑 Qwen 模型。”importrequestsdefgenerate_story(prompt):responserequests.post(http://localhost:11434/api/generate,json{model:qwen2.5:32b,prompt:prompt})returnresponse.json()[response]又是完全不同的接口。小禾又改了两天。第四周客户说“我们公司只能用 Claude合规要求。”importanthropic clientanthropic.Anthropic(api_keyxxx)defgenerate_story(prompt):responseclient.messages.create(modelclaude-sonnet-4-20250514,max_tokens4096,messages[{role:user,content:prompt}])returnresponse.content[0].text小禾崩溃了。4 个星期4 个 LLM业务代码改了 47 处。每次改完还要回归测试生怕哪里漏了。“这日子没法过了。”问题出在哪小禾冷静下来分析发现问题的根源是业务代码和 LLM 实现强耦合。直接调用直接调用直接调用直接调用业务代码OpenAI SDKGemini SDKOllama APIClaude SDK业务代码里到处都是# 生成故事responseclient.chat.completions.create(...)# 生成分镜responseclient.chat.completions.create(...)# 生成角色描述responseclient.chat.completions.create(...)# 生成画面提示词responseclient.chat.completions.create(...)换一次 LLM这些地方全要改。小禾想起了之前学过的设计模式适配器模式。如果在业务代码和 LLM 之间加一层抽象是不是就能解决问题设计统一抽象层小禾画了张新的架构图实现层抽象层业务层OpenAI 适配器Gemini 适配器Ollama 适配器Claude 适配器LLMAdapter 接口生成故事生成分镜生成角色生成提示词业务代码只依赖抽象接口不关心具体用哪个 LLM。切换 LLM换个适配器就行业务代码一行不改。定义统一接口首先定义统一的消息格式和生成接口# app/adapters/llm/base.pyfromabcimportABC,abstractmethodfromtypingimportList,Optional,IteratorfromdataclassesimportdataclassdataclassclassMessage:统一的消息格式role:str# system, user, assistantcontent:strdataclassclassGenerationConfig:生成配置temperature:float0.7max_tokens:Optional[int]Nonestop_sequences:Optional[List[str]]NoneclassLLMAdapter(ABC):LLM 适配器基类abstractmethoddefgenerate(self,messages:List[Message],config:Optional[GenerationConfig]None)-str:生成回复passabstractmethoddefgenerate_stream(self,messages:List[Message],config:Optional[GenerationConfig]None)-Iterator[str]:流式生成passpropertyabstractmethoddefmodel_name(self)-str:模型名称用于日志和调试passpropertydefsupports_streaming(self)-bool:是否支持流式输出returnTrue接口很简单Message统一的消息格式不管哪个 LLM 都用这个GenerationConfig生成参数温度、最大长度等generate一次性生成generate_stream流式生成各平台的差异由各自的适配器处理。实现 OpenAI 适配器# app/adapters/llm/openai_adapter.pyfromopenaiimportOpenAIfromtypingimportList,Optional,Iteratorfrom.baseimportLLMAdapter,Message,GenerationConfigclassOpenAIAdapter(LLMAdapter):OpenAI GPT 系列适配器def__init__(self,api_key:str,model:strgpt-5.1,base_url:Optional[str]None):self.clientOpenAI(api_keyapi_key,base_urlbase_url)self._modelmodeldefgenerate(self,messages:List[Message],config:Optional[GenerationConfig]None)-str:configconfigorGenerationConfig()# 转换为 OpenAI 的消息格式openai_messages[{role:m.role,content:m.content}forminmessages]responseself.client.chat.completions.create(modelself._model,messagesopenai_messages,temperatureconfig.temperature,max_tokensconfig.max_tokens,stopconfig.stop_sequences)returnresponse.choices[0].message.contentdefgenerate_stream(self,messages:List[Message],config:Optional[GenerationConfig]None)-Iterator[str]:configconfigorGenerationConfig()openai_messages[{role:m.role,content:m.content}forminmessages]streamself.client.chat.completions.create(modelself._model,messagesopenai_messages,temperatureconfig.temperature,streamTrue)forchunkinstream:ifchunk.choices[0].delta.content:yieldchunk.choices[0].delta.contentpropertydefmodel_name(self)-str:returnfopenai/{self._model}OpenAI 的适配器最简单因为我们的接口设计本来就参考了 OpenAI 的风格。实现 Gemini 适配器Gemini 的 API 风格不太一样需要做转换# app/adapters/llm/gemini_adapter.pyimportgoogle.generativeaiasgenaifromtypingimportList,Optional,Iteratorfrom.baseimportLLMAdapter,Message,GenerationConfigclassGeminiAdapter(LLMAdapter):Google Gemini 适配器def__init__(self,api_key:str,model:strgemini-3.0-pro):genai.configure(api_keyapi_key)self._model_namemodel self.modelgenai.GenerativeModel(model)defgenerate(self,messages:List[Message],config:Optional[GenerationConfig]None)-str:configconfigorGenerationConfig()# Gemini 的消息格式不同# 需要把 system 消息合并到第一条 user 消息gemini_messagesself._convert_messages(messages)generation_configgenai.GenerationConfig(temperatureconfig.temperature,max_output_tokensconfig.max_tokens,stop_sequencesconfig.stop_sequences)responseself.model.generate_content(gemini_messages,generation_configgeneration_config)returnresponse.textdefgenerate_stream(self,messages:List[Message],config:Optional[GenerationConfig]None)-Iterator[str]:configconfigorGenerationConfig()gemini_messagesself._convert_messages(messages)responseself.model.generate_content(gemini_messages,generation_configgenai.GenerationConfig(temperatureconfig.temperature),streamTrue)forchunkinresponse:ifchunk.text:yieldchunk.textdef_convert_messages(self,messages:List[Message])-List[dict]:转换消息格式result[]system_contentforminmessages:ifm.rolesystem:system_contentm.contentelifm.roleuser:contentm.contentifsystem_content:contentf{system_content}\n\n{content}system_contentresult.append({role:user,parts:[content]})elifm.roleassistant:result.append({role:model,parts:[m.content]})returnresultpropertydefmodel_name(self)-str:returnfgemini/{self._model_name}Gemini 的坑没有 system role要把 system 消息合并到 user 消息里assistant 在 Gemini 里叫 model消息内容要放在 parts 数组里这些差异都被适配器消化了业务代码完全感知不到。实现 Ollama 适配器本地部署的 Ollama用的是 REST API# app/adapters/llm/ollama_adapter.pyimportrequestsimportjsonfromtypingimportList,Optional,Iteratorfrom.baseimportLLMAdapter,Message,GenerationConfigclassOllamaAdapter(LLMAdapter):本地 Ollama 适配器def__init__(self,base_url:strhttp://localhost:11434,model:strqwen2.5:32b):self.base_urlbase_url self._modelmodeldefgenerate(self,messages:List[Message],config:Optional[GenerationConfig]None)-str:configconfigorGenerationConfig()ollama_messages[{role:m.role,content:m.content}forminmessages]responserequests.post(f{self.base_url}/api/chat,json{model:self._model,messages:ollama_messages,options:{temperature:config.temperature,num_predict:config.max_tokens},stream:False},timeout300# 本地模型可能比较慢)response.raise_for_status()returnresponse.json()[message][content]defgenerate_stream(self,messages:List[Message],config:Optional[GenerationConfig]None)-Iterator[str]:configconfigorGenerationConfig()ollama_messages[{role:m.role,content:m.content}forminmessages]responserequests.post(f{self.base_url}/api/chat,json{model:self._model,messages:ollama_messages,options:{temperature:config.temperature},stream:True},streamTrue,timeout300)forlineinresponse.iter_lines():ifline:datajson.loads(line)ifmessageindataandcontentindata[message]:yielddata[message][content]propertydefmodel_name(self)-str:returnfollama/{self._model}Ollama 的好处是消息格式和 OpenAI 兼容转换比较简单。实现 Claude 适配器Claude 有自己的特色# app/adapters/llm/claude_adapter.pyimportanthropicfromtypingimportList,Optional,Iteratorfrom.baseimportLLMAdapter,Message,GenerationConfigclassClaudeAdapter(LLMAdapter):Anthropic Claude 适配器def__init__(self,api_key:str,model:strclaude-sonnet-4-20250514):self.clientanthropic.Anthropic(api_keyapi_key)self._modelmodeldefgenerate(self,messages:List[Message],config:Optional[GenerationConfig]None)-str:configconfigorGenerationConfig()# Claude 的 system 消息要单独传system_msgNoneclaude_messages[]forminmessages:ifm.rolesystem:system_msgm.contentelse:claude_messages.append({role:m.role,content:m.content})kwargs{model:self._model,max_tokens:config.max_tokensor4096,messages:claude_messages,}ifsystem_msg:kwargs[system]system_msgifconfig.temperatureisnotNone:kwargs[temperature]config.temperature responseself.client.messages.create(**kwargs)returnresponse.content[0].textdefgenerate_stream(self,messages:List[Message],config:Optional[GenerationConfig]None)-Iterator[str]:configconfigorGenerationConfig()system_msgNoneclaude_messages[]forminmessages:ifm.rolesystem:system_msgm.contentelse:claude_messages.append({role:m.role,content:m.content})kwargs{model:self._model,max_tokens:config.max_tokensor4096,messages:claude_messages,}ifsystem_msg:kwargs[system]system_msgwithself.client.messages.stream(**kwargs)asstream:fortextinstream.text_stream:yieldtextpropertydefmodel_name(self)-str:returnfanthropic/{self._model}Claude 的坑system 消息要单独传不能放在 messages 里必须指定 max_tokens流式输出的 API 不一样工厂模式统一创建现在有四个适配器了需要一个统一的入口来创建# app/adapters/llm/factory.pyfromtypingimportDict,Type,Optionalfrom.baseimportLLMAdapterfrom.openai_adapterimportOpenAIAdapterfrom.gemini_adapterimportGeminiAdapterfrom.ollama_adapterimportOllamaAdapterfrom.claude_adapterimportClaudeAdapterfromapp.core.configimportsettingsclassLLMFactory:LLM 适配器工厂_adapters:Dict[str,Type[LLMAdapter]]{openai:OpenAIAdapter,gemini:GeminiAdapter,ollama:OllamaAdapter,claude:ClaudeAdapter,}_instance:Optional[LLMAdapter]Noneclassmethoddefcreate(cls,adapter_type:str,**kwargs)-LLMAdapter:创建适配器实例ifadapter_typenotincls._adapters:available, .join(cls._adapters.keys())raiseValueError(fUnknown adapter:{adapter_type}. fAvailable:{available})returncls._adapters[adapter_type](**kwargs)classmethoddefget_default(cls)-LLMAdapter:获取默认适配器单例ifcls._instanceisNone:cls._instancecls._create_from_settings()returncls._instanceclassmethoddef_create_from_settings(cls)-LLMAdapter:从配置创建适配器llm_typesettings.LLM_TYPEifllm_typeopenai:returncls.create(openai,api_keysettings.OPENAI_API_KEY,modelsettings.OPENAI_MODEL)elifllm_typegemini:returncls.create(gemini,api_keysettings.GEMINI_API_KEY,modelsettings.GEMINI_MODEL)elifllm_typeollama:returncls.create(ollama,base_urlsettings.OLLAMA_URL,modelsettings.OLLAMA_MODEL)elifllm_typeclaude:returncls.create(claude,api_keysettings.ANTHROPIC_API_KEY,modelsettings.CLAUDE_MODEL)else:raiseValueError(fUnknown LLM type:{llm_type})classmethoddefregister(cls,name:str,adapter_class:Type[LLMAdapter]):注册新适配器cls._adapters[name]adapter_classclassmethoddefreset(cls):重置单例测试用cls._instanceNone业务代码怎么写现在业务代码变得无比简洁# app/services/story_generator.pyfromapp.adapters.llm.factoryimportLLMFactoryfromapp.adapters.llm.baseimportMessage,GenerationConfigdefgenerate_story(user_prompt:str)-str:生成故事llmLLMFactory.get_default()messages[Message(rolesystem,content你是一个专业的故事创作者擅长写引人入胜的短故事。),Message(roleuser,contentuser_prompt)]returnllm.generate(messages)defgenerate_story_stream(user_prompt:str):流式生成故事llmLLMFactory.get_default()messages[Message(rolesystem,content你是一个专业的故事创作者擅长写引人入胜的短故事。),Message(roleuser,contentuser_prompt)]forchunkinllm.generate_stream(messages):yieldchunk注意看业务代码里没有任何 OpenAI、Gemini、Claude 的影子。它只知道有一个llm可以generate。用的是 GPT-5.1 还是本地 Qwen业务代码不关心也不需要关心。切换模型只改配置现在老板说要换模型小禾只需要# .env 文件# 用 GPT-5.1LLM_TYPEopenaiOPENAI_API_KEYsk-xxxOPENAI_MODELgpt-5.1# 换成 Gemini 3.0LLM_TYPEgeminiGEMINI_API_KEYxxxGEMINI_MODELgemini-3.0-pro# 换成本地 OllamaLLM_TYPEollamaOLLAMA_URLhttp://localhost:11434OLLAMA_MODELqwen2.5:32b# 换成 ClaudeLLM_TYPEclaudeANTHROPIC_API_KEYxxxCLAUDE_MODELclaude-sonnet-4-20250514改一行配置重启服务完事。业务代码一行不改。加个新模型要多久后来老板说要支持某个客户自己的私有模型。小禾花了半小时写了个新适配器# app/adapters/llm/custom_adapter.pyclassCustomLLMAdapter(LLMAdapter):客户私有模型适配器def__init__(self,endpoint:str,api_key:str):self.endpointendpoint self.api_keyapi_keydefgenerate(self,messages,configNone):# 调用客户的 APIresponserequests.post(self.endpoint,headers{Authorization:fBearer{self.api_key}},json{messages:[{role:m.role,content:m.content}forminmessages]})returnresponse.json()[result]# ... 其他方法然后注册一下LLMFactory.register(custom,CustomLLMAdapter)配置文件加一行LLM_TYPEcustom搞定。复盘总结小禾算了笔账指标改造前改造后切换 LLM 改动量47 处1 行配置切换 LLM 耗时2 天2 分钟新增 LLM 耗时2 天30 分钟业务代码耦合强耦合零耦合单元测试难度困难简单可 mock老板再也不能用换个模型来折腾他了。小禾的感悟变化是永恒的 代码要为变化而设计。 今天是 GPT 明天是 Gemini 后天是什么 谁也不知道。 但有了适配器 我不再害怕。 业务代码只知道接口 不知道实现 这就是解耦的力量。 抽象不是过度设计 是对未来的保险。 当老板说换个模型时 我终于可以微笑着说 好的稍等两分钟。小禾关掉 IDE心情舒畅。以后不管换多少次模型他都不怕了。下一篇预告显存爆了服务挂了半夜被叫起来GPU 资源管理不是加显存就能解决的。敬请期待。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

中创动力网站建设wordpress给用户注册

Wan2.2-T2V-5B能否生成历史事件重现?文博数字化尝试 你有没有想过,仅凭一段古籍里的文字描述,就能“复活”千年前的市井烟火? 比如《梦溪笔谈》中一句“街鼓鸣,百官启行”,能不能变成一段清晨长安城坊门开启…

张小明 2025/12/29 4:31:27 网站建设

服装公司网站模版网页制作培训班前景

DBAN(Dariks Boot and Nuke)是一款专业的开源数据擦除工具,专门设计用于安全擦除计算机硬盘上的重要数据。这款免费工具能够彻底删除硬盘内容,确保您的信息在设备退役前得到妥善处理。 【免费下载链接】dban Unofficial fork of D…

张小明 2025/12/29 4:31:26 网站建设

安平百度做网站企业网站美工设计

Source Han Serif 思源宋体:开源专业字体全方位应用指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 还在为商业项目中的字体版权问题而焦虑吗?Source Han S…

张小明 2025/12/29 4:31:27 网站建设

网站建设实训课深圳网站建设全包

【网络安全入门】学习网络安全必须知道的100 个网络基础知识 什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备通信的电缆类型和协议。 2 OSI 参考模型的层次是什么? 有 7 个 OSI 层:物理层,数据链路层,网…

张小明 2025/12/29 4:31:29 网站建设

做网站需要学会什么软件免费虚拟主机网站

🎯 3 分钟读完本文,你将获得: 一套开箱即用的企业级 FastAPI 项目架构解决 FastAPI 项目从 Demo 到生产的所有痛点掌握分层架构、自动迁移、缓存等最佳实践节省至少 2 周的项目搭建时间 😫 你是否遇到过这些问题? 场景…

张小明 2025/12/29 4:31:28 网站建设

百度收录网站有什么好处中国住房和城乡建设局官网

深度学习从零开始:终极免费教程与实战指南 【免费下载链接】deep_learning_from_scratch 《深度学习入门——基于Python的理论与实现》作者:斋藤康毅 译者:陆宇杰 项目地址: https://gitcode.com/gh_mirrors/de/deep_learning_from_scratch…

张小明 2025/12/30 8:56:44 网站建设