1. 项目概述一个面向开发者的AI应用快速构建框架最近在折腾AI应用开发的朋友应该都体会过那种“想法很丰满实现很骨感”的尴尬。你想做一个能根据用户描述生成特定风格图片的机器人或者一个能理解复杂指令并执行多步骤任务的智能助手。当你兴冲冲地打开文档准备大干一场时迎面而来的往往是不同AI模型API的调用方式五花八门、消息历史管理繁琐、工具Tools和函数调用Function Calling的集成让人头大、前后端联调又是一地鸡毛。最终大量时间都花在了搭建基础设施和解决兼容性问题上核心的业务逻辑和创新点反而被搁置了。lss233/kirara-ai这个项目就是为了解决这个痛点而生的。你可以把它理解为一个“AI应用乐高”或者“AI领域的Next.js”。它的核心目标不是提供一个现成的、功能固定的AI产品而是为开发者提供一套高度模块化、开箱即用、且易于扩展的框架让你能像搭积木一样快速构建起属于自己的、功能复杂的AI应用。无论是想做一个集成多种模型如OpenAI GPT、Claude、本地部署的Llama等的聊天界面还是一个能调用搜索引擎、查询数据库、执行代码的智能体Agent甚至是需要复杂工作流编排的自动化任务Kirara都试图通过清晰的抽象和丰富的组件降低你的开发门槛。这个项目适合谁呢首先是有一定Python基础的开发者无论是全栈、后端还是AI算法工程师只要你需要在产品中集成AI能力Kirara都能显著提升你的开发效率。其次对于想要学习现代AI应用架构的学生或爱好者通过研究和拆解这个框架你能系统地理解一个生产级AI应用是如何处理会话、工具调用、流式输出等核心概念的。简单来说如果你厌倦了每次都要从零开始写openai.ChatCompletion.create并处理随之而来的各种胶水代码那么Kirara值得你花时间了解一下。2. 核心架构与设计哲学解析2.1 为什么是“框架”而非“库”理解Kirara的第一步是分清“框架”和“库”的区别。一个库Library像是工具箱你在自己的程序里按需调用里面的扳手或锤子函数程序的控制流掌握在你自己手中。而一个框架Framework则更像一套房子的骨架它定义了整体的结构和流程你是在它划定的房间里进行装修和布置控制流由框架主导。Kirara是一个框架。它为你预设了AI应用的核心运行范式接收输入、管理会话上下文、路由到合适的处理器可能是不同的模型或工具、执行处理、返回输出。你作为开发者主要工作不再是操心“怎么把消息发给API并拿回结果”这种底层细节而是专注于在Kirara提供的“房间”里配置和实现你自己的“业务逻辑”。比如定义你的AI助手能使用哪些工具Tools定制对话的前后处理逻辑或者设计复杂的工作流。这种设计哲学带来的最大好处是一致性和可维护性。无论你的应用多复杂其底层的数据流、错误处理、日志记录都可以遵循统一的模式新人接手项目也能快速理解架构。2.2 核心抽象会话、消息与工具Kirara的架构围绕几个核心抽象构建理解它们就理解了整个框架的脉络。会话Session这是最顶层的抽象代表一次完整的交互上下文。一个会话包含了一次对话从开始到结束的所有信息。在Kirara中会话负责维护整个对话的状态包括用户身份、环境变量、以及最重要的——消息历史。会话对象确保了对话的连续性使得AI能够拥有“记忆”。消息Message这是对话的基本单元。Kirara通常会遵循OpenAI的格式将消息区分为不同的角色Role例如user: 用户发送的消息。assistant: AI助手回复的消息。system: 系统指令用于设定AI的行为模式、角色设定等。tool或function: 代表工具调用的输入或输出。Kirara的消息管理不仅限于存储更重要的是处理消息的序列化、上下文窗口Token的计算与截断以及在不同模型提供商之间的格式适配。例如将内部消息格式转换为Anthropic Claude API所需的格式或者转换为本地Llama模型需要的提示词模板。工具Tool这是Kirara实现智能体Agent能力的关键。一个工具本质上是一个可以被AI模型调用的函数。AI模型在分析用户请求后如果判断需要调用外部能力如查询天气、计算、搜索网络它就会在回复中声明要调用某个工具并给出调用参数。Kirara的运行时捕获到这个声明后会执行对应的函数并将执行结果以tool角色的消息格式重新放回对话历史供AI模型生成最终的用户回复。例如你定义了一个get_current_weather的工具函数。当用户问“北京天气怎么样”时AI模型可能会回复一个结构化的调用请求“我需要调用get_current_weather工具参数是location’北京’。” Kirara框架会拦截这个请求执行真实的天气查询函数然后将查询结果“北京今天晴25度”作为信息补充给AIAI再组织成自然语言回复给用户“北京今天天气晴朗气温25摄氏度左右。”Kirara的强大之处在于它提供了一套优雅的装饰器或类机制让你能非常轻松地将一个普通的Python函数“包装”成一个AI可识别和调用的工具并自动处理参数的类型验证、文档生成描述这个工具是做什么的AI靠这个描述来决定是否调用以及调用流程。2.3 提供者Provider抽象统一的多模型接口AI世界是碎片化的OpenAI的GPT系列、Anthropic的Claude、Google的Gemini还有无数开源的Llama、Qwen等模型它们的API接口、参数命名、响应格式各有不同。如果你的应用代码里写满了针对不同API的适配代码将会是一场维护噩梦。Kirara通过提供者Provider抽象层解决了这个问题。它为“调用一个AI模型完成对话”这个操作定义了一个统一的接口。无论底层是OpenAI、Azure OpenAI、还是通过Ollama部署的本地模型在Kirara的上层业务逻辑看来它们都是一样的一个可以接收消息列表并返回AI回复的组件。当你需要切换模型时例如从昂贵的GPT-4降级到更经济的GPT-3.5-Turbo或者从云端模型切换到本地模型以保障数据隐私你只需要在配置中更改提供者的类型和参数而不需要重写任何核心的业务逻辑代码。这种设计极大地提升了应用的灵活性和未来兼容性。3. 快速上手构建你的第一个AI智能体理论说了这么多我们直接动手用Kirara快速搭建一个能查询天气和计算数学的简易智能体。这个过程会让你对Kirara的工作流有一个直观的感受。3.1 环境准备与安装首先确保你的Python环境在3.8以上。创建一个新的虚拟环境是一个好习惯。python -m venv kirara-env source kirara-env/bin/activate # Linux/macOS # 或 kirara-env\Scripts\activate # Windows然后安装Kirara。由于它可能处于活跃开发中我们通常直接从GitHub仓库安装最新版本。pip install githttps://github.com/lss233/kirara-ai.git此外我们还需要安装openai库因为我们的例子将使用OpenAI的模型。同时我们将用一个虚拟的天气查询工具来演示所以还会安装requests来模拟网络请求。pip install openai requests别忘了设置你的OpenAI API密钥。你可以将它设置为环境变量。export OPENAI_API_KEY你的-api-key-here注意在生产环境中切勿将API密钥硬编码在代码中。使用环境变量或安全的密钥管理服务是必须的。3.2 定义你的工具集工具是智能体的“手脚”。我们来定义两个简单的工具。工具一获取天气这是一个模拟工具它并不真正调用天气API而是返回一个固定的字符串。在实际应用中你可以在这里集成任何真实的API比如和风天气、OpenWeatherMap等。# tools.py import kirara from pydantic import BaseModel, Field # 首先定义工具的输入参数模型。这利用了Pydantic能提供强大的类型检查和数据验证。 # AI模型会参考这个模型的字段定义来生成调用参数。 class WeatherQueryInput(BaseModel): location: str Field(description城市名称例如北京、上海) unit: str Field(defaultcelsius, description温度单位celsius 或 fahrenheit) # 使用Kirara的装饰器 kirara.tool 来声明这是一个工具。 # 装饰器会自动提取函数的文档字符串docstring和参数模型生成给AI看的工具描述。 kirara.tool(args_schemaWeatherQueryInput) def get_current_weather(location: str, unit: str celsius) - str: 根据城市名称获取当前的天气信息。 # 模拟一个API调用和响应 # 真实场景下这里会是 requests.get(...) print(f[工具调用] 查询天气: 地点{location}, 单位{unit}) return f{location}的天气是晴朗气温22{unit}。 # 工具二计算器 class CalculatorInput(BaseModel): expression: str Field(description一个有效的数学表达式例如3 5 * 2) kirara.tool(args_schemaCalculatorInput) def calculate(expression: str) - str: 计算一个数学表达式的结果。 注意出于安全考虑在实际生产中应使用更安全的评估方法如 ast.literal_eval 或专用数学库。 print(f[工具调用] 计算表达式: {expression}) try: # 警告使用eval有安全风险仅用于演示。 # 在生产中务必替换为安全的计算方式 result eval(expression) return f表达式 {expression} 的计算结果是 {result}。 except Exception as e: return f计算表达式 {expression} 时出错{e}关键点解析Pydantic模型WeatherQueryInput和CalculatorInput类定义了工具所需的参数。Field中的description字段至关重要AI模型如GPT正是通过阅读这些描述来理解何时以及如何使用这个工具。装饰器kirara.tool这是Kirara的魔法所在。它自动将你的Python函数注册到框架中并为其生成符合OpenAI Function Calling或类似规范的元数据名称、描述、参数模式。安全警告示例中的calculate工具使用了eval()这在生产环境是极度危险的因为它会执行任意代码。这里仅作演示。正确的做法是使用ast.literal_eval仅支持常量表达式或像numexpr这样的安全计算库。3.3 配置与运行智能体现在我们将工具、模型和会话组合起来。# main.py import asyncio import kirara from kirara.providers import OpenAIProvider from tools import get_current_weather, calculate # 导入我们定义的工具 async def main(): # 1. 创建一个模型提供者这里使用OpenAI的gpt-3.5-turbo模型 # 你可以轻松替换为其他提供者如ClaudeProvider、OllamaProvider等。 provider OpenAIProvider( modelgpt-3.5-turbo, api_key你的-api-key # 更推荐从环境变量读取 ) # 2. 创建一个智能体Agent它是模型和工具的结合体。 # 我们将定义的工具列表传给它。 agent kirara.Agent( providerprovider, tools[get_current_weather, calculate], # 注册工具 system_message你是一个乐于助人的助手可以回答问题和使用工具。请根据用户需求在需要时主动使用你拥有的工具。, # 系统指令设定AI角色 ) # 3. 创建一个会话Session它代表一次独立的对话。 session agent.create_session() # 4. 开始对话循环 print(智能体已启动输入 退出 或 quit 来结束对话。) while True: try: user_input input(\n你: ) if user_input.lower() in [退出, quit, exit]: print(对话结束。) break # 使用会话的 run 方法处理用户输入。 # 这是一个异步方法它会 # a. 将用户输入追加到会话历史。 # b. 将整个历史包括可能的工具调用结果发送给AI模型。 # c. 如果AI返回工具调用请求则自动执行对应工具并将结果追加回历史。 # d. 重复b-c直到AI返回一个面向用户的文本回复。 # e. 流式输出最终回复。 print(助手: , end, flushTrue) async for chunk in session.run(user_input, streamTrue): # 流式输出逐个打印文本块实现打字机效果。 print(chunk, end, flushTrue) print() # 换行 except KeyboardInterrupt: print(\n对话被中断。) break except Exception as e: print(f\n发生错误: {e}) if __name__ __main__: asyncio.run(main())运行与交互 保存以上代码后运行python main.py。你会看到一个简单的命令行聊天界面。尝试输入以下内容“北京天气怎么样” - 观察控制台你会看到[工具调用]的日志然后AI会整合工具返回的结果给出回复。“计算一下3的平方加上4的平方等于多少” - AI会调用计算器工具。“先告诉我上海天气再计算(1020)/3的结果。” - 这是一个多轮工具调用的复杂请求Kirara的会话机制会自动管理整个流程。这个简单的例子展示了Kirara如何将模型调用、工具执行、会话管理这些繁琐的任务封装起来让你只需关注工具函数本身和业务逻辑。4. 深入核心会话管理与上下文工程4.1 会话的生命周期与状态管理在Kirara中Session对象是对话的核心容器。理解它的生命周期对于构建稳定应用至关重要。创建与初始化当你调用agent.create_session()时Kirara会创建一个新的、独立的会话对象。这个会话会继承智能体Agent的配置如系统消息、可用工具列表但拥有自己独立的对话历史内存。这意味着你可以用同一个Agent配置同时服务成千上万个用户每个用户的对话都是隔离的。消息历史的存储会话内部维护着一个消息列表。每次用户输入、AI回复、工具调用和工具返回结果都会被转换成特定格式的Message对象并按顺序追加到这个列表中。这个列表就是模型的“上下文”。Kirara会自动处理消息的格式转换以适配底层模型提供者的要求。上下文窗口与截断策略所有AI模型都有上下文长度限制如GPT-3.5-Turbo的16K Token。当对话历史越来越长超过模型限制时必须进行截断。Kirara内置了智能的截断策略。常见的策略包括丢弃最旧的对话这是最简单的方式但可能导致AI忘记很早之前的重要设定。优先保留系统消息和最近对话系统消息通常包含核心指令必须保留。同时保留最近几轮对话以保证连贯性。基于重要性的摘要更高级的策略是当历史过长时调用另一个AI模型对早期对话进行摘要然后将摘要作为一条新消息放入历史替代原有的大段内容。Kirara的架构允许你自定义这种ContextTruncator上下文截断器。会话的持久化对于Web应用会话需要被保存到数据库如Redis、PostgreSQL或文件中以便用户下次访问时能恢复对话。Kirara的Session对象通常可以被序列化如转换成JSON。你需要自己实现存储和加载的逻辑在用户连接时从存储中还原出会话对象。框架本身关注内存中的会话管理而持久化是应用层需要结合Web框架如FastAPI来实现的。4.2 系统消息System Prompt的设计艺术系统消息是塑造AI行为的“宪法”。一句好的系统提示词能让AI的输出质量有质的提升。在Kirara中你可以在创建Agent时通过system_message参数设置。基础角色设定agent kirara.Agent( providerprovider, system_message你是一个专业、友好且简洁的客服助手。你的主要职责是解答用户关于产品X的问题。如果遇到不知道的问题请如实告知并引导用户联系人工客服。不要编造信息。 )复杂指令与约束 对于更复杂的智能体你可能需要更详细的指令包括输出格式、禁止事项、推理步骤等。system_prompt 你是一个数据分析助手。请遵循以下规则 1. 当用户询问数据时首先思考需要调用哪个工具如 query_database, calculate_statistics。 2. 所有数值结果请保留两位小数。 3. 在回复的最后用‘---’分隔并附上一句对数据趋势的简短洞察。 4. 绝对不要对数据做出无法由工具结果支持的预测性结论。 动态系统消息有时系统消息需要根据会话上下文动态变化。例如在聊天过程中用户选择了“专家模式”你可能需要在系统消息中追加更专业的指令。Kirara允许你在会话层面覆盖或追加系统消息。你可以通过修改session.messages列表在最前面插入或更新一条role’system’的消息来实现。但要注意这可能会影响模型的上下文理解需谨慎操作。实操心得系统消息的调优是一个迭代过程。不要指望一次写完美。在实际测试中观察AI的“错误”回答思考是哪个指令没被遵守然后有针对性地修改系统提示词。一个技巧是在系统消息中明确要求AI“逐步思考”Think step by step这往往能提高其复杂任务上的表现。5. 高级特性工作流编排与自定义扩展当你的AI应用超越简单的“一问一答加工具调用”需要处理更复杂的、多步骤的、有条件分支的业务流程时就需要用到工作流Workflow编排。Kirara在这方面也提供了强大的支持。5.1 理解工作流Workflow工作流将一系列步骤Step组织成一个有向图。每个步骤可以是一个简单的AI调用、一个工具调用、一个条件判断甚至是另一个子工作流。步骤之间通过数据流连接前一个步骤的输出可以作为后一个步骤的输入。假设我们要构建一个“智能内容创作助手”工作流步骤A头脑风暴根据用户给的主题让AI生成5个文章标题。步骤B选择让用户从5个标题中手动选择一个这是一个需要人工干预的步骤。步骤C大纲生成根据选定的标题让AI生成详细的文章大纲。步骤D段落撰写根据大纲并行或依次让AI生成每个段落的内容。步骤E校对对生成的完整文章进行语法和风格校对。在Kirara中你可以用代码或声明式的方式来定义这样的工作流。框架会负责步骤的执行、状态的传递、错误的处理以及异步操作的协调。5.2 实现一个自定义工具集成外部知识库工具Tool是Kirara扩展性的基石。除了简单的函数工具可以非常复杂。让我们实现一个与外部向量数据库如Chroma、Pinecone集成的工具让AI具备检索私有知识的能力。import kirara from pydantic import BaseModel, Field from typing import List # 假设我们使用 sentence-transformers 进行文本嵌入使用 chromadb 作为向量库 from sentence_transformers import SentenceTransformer import chromadb class KnowledgeQueryInput(BaseModel): question: str Field(description用户提出的问题用于从知识库中检索相关信息) top_k: int Field(default3, description返回最相关的几条知识片段) kirara.tool(args_schemaKnowledgeQueryInput) def search_knowledge_base(question: str, top_k: int 3) - str: 从内部知识库中检索与问题最相关的文档片段。 返回检索到的文本内容供助手参考以回答问题。 # 1. 加载嵌入模型生产环境应缓存模型避免每次加载 embed_model SentenceTransformer(all-MiniLM-L6-v2) # 2. 将用户问题转换为向量 query_embedding embed_model.encode(question).tolist() # 3. 连接向量数据库 chroma_client chromadb.PersistentClient(path./chroma_db) collection chroma_client.get_or_create_collection(namecompany_docs) # 4. 执行相似度搜索 results collection.query( query_embeddings[query_embedding], n_resultstop_k ) # 5. 格式化检索结果 if results and results[documents]: retrieved_docs results[documents][0] formatted_context \n\n--- 参考知识 ---\n for i, doc in enumerate(retrieved_docs): formatted_context f[片段{i1}]: {doc}\n return formatted_context else: return [知识库检索] 未找到相关文档。 # 在创建Agent时将这个工具加入列表 agent kirara.Agent( providerprovider, tools[search_knowledge_base, get_current_weather, calculate], system_message你是一个拥有公司内部知识库访问权限的助手。当用户询问公司政策、产品细节或历史文档时请优先使用‘search_knowledge_base’工具获取准确信息然后基于检索到的信息进行回答。 )现在当用户询问“我们公司的年假政策是什么”时AI会先调用search_knowledge_base工具从向量数据库中检索出相关的员工手册片段然后基于这些确凿的信息生成回答避免了AI“胡编乱造”公司政策的情况。性能与优化注意嵌入模型缓存每次调用都加载模型极其低效。应在应用启动时全局加载一次或使用模型服务。数据库连接池对于高频应用需要管理数据库连接避免频繁创建和断开。超时与重试网络调用可能失败。工具函数内部应包含健壮的错误处理和重试逻辑并向AI返回清晰的错误信息例如“知识库暂时无法访问请稍后再试”。Token消耗检索到的文档可能很长全部放入上下文会消耗大量Token。可以考虑让工具本身集成一个摘要功能只返回最核心的几句话。5.3 自定义模型提供者Provider虽然Kirara内置了主流模型的提供者但你可能需要接入一个自定义的或更新的模型API。实现一个自定义提供者并不复杂关键是实现标准的接口。from typing import List, AsyncGenerator import kirara from kirara.models import Message class MyCustomLLMProvider(kirara.Provider): 一个自定义的本地模型提供者示例。 def __init__(self, model_path: str, api_base: str http://localhost:11434): self.model_path model_path self.api_base api_base async def chat_completion( self, messages: List[Message], stream: bool False, **kwargs ) - AsyncGenerator[str, None]: 核心方法接收消息历史调用模型返回回复。 需要处理流式和非流式两种模式。 # 1. 将Kirara的通用Message格式转换为你的模型API所需的格式 formatted_messages self._format_messages(messages) # 2. 准备API请求参数 payload { model: self.model_path, messages: formatted_messages, stream: stream, **kwargs # 传递其他参数如temperature, max_tokens等 } # 3. 发起请求这里以requests为例生产环境应用aiohttp import requests url f{self.api_base}/v1/chat/completions # 假设API兼容OpenAI格式 if stream: # 流式处理 with requests.post(url, jsonpayload, streamTrue) as response: response.raise_for_status() for line in response.iter_lines(): if line: # 解析流式返回的数据块提取delta content chunk self._parse_stream_chunk(line) if chunk: yield chunk else: # 非流式处理 response requests.post(url, jsonpayload) response.raise_for_status() result response.json() # 从result中提取完整的回复文本 full_content self._extract_content(result) # 对于非流式我们也可以包装成异步生成器只yield一次 yield full_content def _format_messages(self, messages: List[Message]) - list: # 实现格式转换逻辑例如转成Ollama、Anthropic等API要求的格式 formatted [] for msg in messages: # 这里需要根据目标API调整 formatted.append({role: msg.role, content: msg.content}) return formatted def _parse_stream_chunk(self, line: bytes) - str: # 解析流式响应行例如OpenAI格式是data: {...} # 这里需要根据目标API的流式响应格式来具体实现 # 伪代码解析JSON返回 choices[0].delta.content return parsed_chunk def _extract_content(self, result: dict) - str: # 从非流式响应结果中提取文本内容 # 伪代码返回 result[choices][0][message][content] return result.get(choices, [{}])[0].get(message, {}).get(content, )实现自定义提供者后你就可以像使用OpenAIProvider一样使用它my_provider MyCustomLLMProvider(model_pathqwen2.5:7b) agent kirara.Agent(providermy_provider, tools[...])这为Kirara接入了几乎无限的模型可能性无论是通过Ollama运行的本地模型还是企业内网的私有模型服务都可以轻松集成。6. 生产环境部署与性能调优将基于Kirara开发的原型应用部署到生产环境需要考虑一系列工程化问题。6.1 异步Async与并发处理Kirara的核心API设计是异步的使用async/await这是为了高效处理高并发的AI请求因为网络I/O调用模型API是主要的耗时操作。在Web服务器如FastAPI、Sanic中使用时务必确保你的请求处理函数也是异步的并正确awaitKirara的调用。from fastapi import FastAPI from pydantic import BaseModel app FastAPI() # 假设agent已在全局初始化 agent None # 实际应在启动时初始化 class ChatRequest(BaseModel): session_id: str message: str app.post(/chat) async def chat_endpoint(request: ChatRequest): # 1. 根据session_id从数据库或缓存中加载会话 session await load_session_from_db(request.session_id) if session is None: session agent.create_session() await save_session_to_db(request.session_id, session) # 2. 处理用户消息 full_response async for chunk in session.run(request.message, streamTrue): full_response chunk # 如果是真正的流式HTTP响应这里应该用Server-Sent Events (SSE)逐个发送chunk # await sse_queue.put(chunk) # 3. 保存更新后的会话 await save_session_to_db(request.session_id, session) # 4. 返回响应这里简化为例一次性返回 return {response: full_response}关键点会话存储load_session_from_db和save_session_to_db需要你自己实现可以使用Redis存储序列化的会话对象或数据库。流式响应对于更好的用户体验应该实现Server-Sent Events (SSE)来实时推送AI生成的每一个词。上述示例中的async for循环非常适合与SSE结合。全局AgentWeb应用中Agent实例包含模型配置和工具通常作为全局单例或依赖项初始化避免每次请求都重复创建。6.2 错误处理与重试机制生产环境中网络波动、模型API限流或临时故障是常态。你的应用必须具备韧性。工具调用重试在自定义工具函数内部对于可能失败的外部API调用如数据库查询、第三方服务应使用tenacity等库实现带退避策略的重试。from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def call_unstable_external_api(): # ...模型API调用重试Kirara的Provider层可能已经内置了部分重试逻辑。如果没有你可以在自定义Provider的chat_completion方法中实现或者使用一个包装器。OpenAI的官方库就有自动重试选项。优雅降级当核心工具或模型完全不可用时应有备用方案。例如知识库检索工具失败时可以配置AI直接回复“知识库暂时无法访问我将基于通用知识为您解答”并避免触发需要该工具的问题。6.3 监控、日志与成本控制结构化日志记录关键事件如会话创建、工具调用参数、结果、耗时、模型调用请求Token数、响应Token数、耗时。这有助于调试和成本分析。import logging import time logger logging.getLogger(__name__) kirara.tool async def some_tool(...): start_time time.time() # ... 工具逻辑 duration time.time() - start_time logger.info(fTool some_tool executed, extra{duration: duration, params: ...})Token消耗监控AI模型API的成本直接与Token消耗挂钩。Kirara内部可能会计算消息的Token数或依赖模型API返回的用量。你应该在每次对话后记录这些数据并设置告警防止因意外循环或攻击导致成本激增。性能剖析使用像cProfile或async-profiler这样的工具定期分析应用的性能瓶颈。常见瓶颈可能在于工具函数的网络延迟、向量数据库检索速度、大会话历史的Token计算等。针对性地进行优化比如为工具添加缓存、优化检索索引、实施更积极的会话摘要策略。7. 常见问题与排查技巧实录在实际开发和运维中你肯定会遇到各种问题。以下是一些典型场景及其解决思路。7.1 AI不调用工具现象你明明定义了工具并在Agent中注册了但AI在回答相关问题时完全无视工具只用自己的知识回答可能还会胡编乱造。排查步骤检查工具描述AI完全依赖你在kirara.tool装饰器中自动生成的函数文档字符串docstring和Field(description)来理解工具。确保描述清晰、准确说明了工具的用途、适用场景和参数含义。模糊的描述会导致AI无法正确匹配。检查系统消息系统消息中是否明确鼓励或要求AI使用工具例如加入“请优先使用你拥有的工具来获取准确信息”这样的指令。检查模型能力你使用的模型是否支持“函数调用”Function Calling或“工具使用”Tool Usegpt-3.5-turbo和gpt-4系列都支持。但一些更小、更老的模型或某些开源模型可能不支持。确保你的Provider配置的模型具备此能力。开启调试日志查看Kirara或底层模型库的调试输出看AI的回复中是否包含了工具调用的请求。有时AI可能以自然语言说“我将为你查询天气”但并没有输出结构化的工具调用请求。这需要你调整提示词。简化测试用一个极其明确、工具唯一能解决的问题测试如“请使用计算器工具计算12345乘以67890”。如果这都不调用那问题肯定出在配置或模型上。7.2 流式输出中断或不流畅现象在Web前端使用SSE接收流式响应时经常中断或者内容是一大段一起出来没有“打字机”效果。排查步骤检查网络与代理确保服务器到客户端之间没有代理或防火墙对流式响应text/event-stream进行缓冲或干扰。一些云服务商的负载均衡器默认配置可能会缓冲响应。检查服务器端生成速度在服务器端代码中检查async for chunk in session.run(...)循环内chunk的大小。如果模型API返回的块本身就很大比如一整句话那么流式效果就不明显。同时确保在yield每个chunk后立即刷新flush。前端SSE实现检查前端EventSource的实现是否正确处理了onmessage事件并是否在连接断开时尝试重连。超时设置服务器和客户端的读写超时时间要设置得足够长。AI生成一段长文本可能需要数十秒任何一方的超时设置过短都会导致连接断开。7.3 会话历史过长导致响应慢或API错误现象随着对话轮数增加应用响应越来越慢最终可能收到模型API返回的“上下文长度超限”错误。解决方案启用自动截断确认你使用的Kirara版本或Provider配置是否开启了自动上下文截断。查看相关配置项如max_tokens、max_context_length。实现自定义截断策略如果内置策略不满足需求例如你需要永久记住用户的某个关键信息可以实现一个自定义的ContextTruncator。例如在截断时永远保留第一条系统消息和最近N条用户/助手对话同时如果历史中包含标记为“重要”的消息也予以保留。主动进行会话摘要在对话达到一定长度后可以主动触发一个“摘要”步骤。调用另一个AI模型如更便宜、更快的模型将早期的对话历史总结成一段简洁的文字然后用这段摘要替换掉大段旧历史。Kirara的工作流功能非常适合编排这个自动化过程。优化工具返回内容工具函数应返回精炼、相关的信息避免返回冗长的HTML、JSON原始数据或错误堆栈。在工具内部就对数据进行清洗和摘要。7.4 工具函数执行慢阻塞整个会话现象某个工具如调用一个慢速的外部API执行需要10秒钟导致整个聊天响应卡住。解决方案异步工具将工具函数定义为async def并在其内部使用异步HTTP客户端如aiohttp进行网络调用。这样在等待外部API响应时事件循环可以处理其他任务。kirara.tool async def slow_network_tool(...): async with aiohttp.ClientSession() as session: async with session.get(slow_api_url) as response: data await response.json() return process(data)设置超时为工具调用设置超时。可以使用asyncio.wait_for来包装工具调用如果超时则向AI返回一个超时提示而不是让用户无限等待。try: result await asyncio.wait_for(slow_network_tool(...), timeout5.0) except asyncio.TimeoutError: return “请求外部服务超时请稍后重试。”缓存结果对于幂等的、结果变化不频繁的工具调用如根据城市查询天气结果在短时间内是相同的可以添加缓存层如使用functools.lru_cache或Redis在缓存有效期内直接返回缓存结果极大提升响应速度。开发基于Kirara的AI应用是一个将创意快速落地的愉快过程。它通过精心的抽象把脏活累活都揽了过去让你能聚焦在构建有价值的工具和设计巧妙的交互流程上。从简单的问答机器人到复杂的多智能体协作系统这个框架都能提供坚实的支撑。当然就像任何框架一样深入使用后你可能会遇到需要定制和绕过的场景但它的设计总体上足够灵活能够容纳这些高级需求。
Kirara AI:模块化框架助力开发者快速构建AI应用与智能体
1. 项目概述一个面向开发者的AI应用快速构建框架最近在折腾AI应用开发的朋友应该都体会过那种“想法很丰满实现很骨感”的尴尬。你想做一个能根据用户描述生成特定风格图片的机器人或者一个能理解复杂指令并执行多步骤任务的智能助手。当你兴冲冲地打开文档准备大干一场时迎面而来的往往是不同AI模型API的调用方式五花八门、消息历史管理繁琐、工具Tools和函数调用Function Calling的集成让人头大、前后端联调又是一地鸡毛。最终大量时间都花在了搭建基础设施和解决兼容性问题上核心的业务逻辑和创新点反而被搁置了。lss233/kirara-ai这个项目就是为了解决这个痛点而生的。你可以把它理解为一个“AI应用乐高”或者“AI领域的Next.js”。它的核心目标不是提供一个现成的、功能固定的AI产品而是为开发者提供一套高度模块化、开箱即用、且易于扩展的框架让你能像搭积木一样快速构建起属于自己的、功能复杂的AI应用。无论是想做一个集成多种模型如OpenAI GPT、Claude、本地部署的Llama等的聊天界面还是一个能调用搜索引擎、查询数据库、执行代码的智能体Agent甚至是需要复杂工作流编排的自动化任务Kirara都试图通过清晰的抽象和丰富的组件降低你的开发门槛。这个项目适合谁呢首先是有一定Python基础的开发者无论是全栈、后端还是AI算法工程师只要你需要在产品中集成AI能力Kirara都能显著提升你的开发效率。其次对于想要学习现代AI应用架构的学生或爱好者通过研究和拆解这个框架你能系统地理解一个生产级AI应用是如何处理会话、工具调用、流式输出等核心概念的。简单来说如果你厌倦了每次都要从零开始写openai.ChatCompletion.create并处理随之而来的各种胶水代码那么Kirara值得你花时间了解一下。2. 核心架构与设计哲学解析2.1 为什么是“框架”而非“库”理解Kirara的第一步是分清“框架”和“库”的区别。一个库Library像是工具箱你在自己的程序里按需调用里面的扳手或锤子函数程序的控制流掌握在你自己手中。而一个框架Framework则更像一套房子的骨架它定义了整体的结构和流程你是在它划定的房间里进行装修和布置控制流由框架主导。Kirara是一个框架。它为你预设了AI应用的核心运行范式接收输入、管理会话上下文、路由到合适的处理器可能是不同的模型或工具、执行处理、返回输出。你作为开发者主要工作不再是操心“怎么把消息发给API并拿回结果”这种底层细节而是专注于在Kirara提供的“房间”里配置和实现你自己的“业务逻辑”。比如定义你的AI助手能使用哪些工具Tools定制对话的前后处理逻辑或者设计复杂的工作流。这种设计哲学带来的最大好处是一致性和可维护性。无论你的应用多复杂其底层的数据流、错误处理、日志记录都可以遵循统一的模式新人接手项目也能快速理解架构。2.2 核心抽象会话、消息与工具Kirara的架构围绕几个核心抽象构建理解它们就理解了整个框架的脉络。会话Session这是最顶层的抽象代表一次完整的交互上下文。一个会话包含了一次对话从开始到结束的所有信息。在Kirara中会话负责维护整个对话的状态包括用户身份、环境变量、以及最重要的——消息历史。会话对象确保了对话的连续性使得AI能够拥有“记忆”。消息Message这是对话的基本单元。Kirara通常会遵循OpenAI的格式将消息区分为不同的角色Role例如user: 用户发送的消息。assistant: AI助手回复的消息。system: 系统指令用于设定AI的行为模式、角色设定等。tool或function: 代表工具调用的输入或输出。Kirara的消息管理不仅限于存储更重要的是处理消息的序列化、上下文窗口Token的计算与截断以及在不同模型提供商之间的格式适配。例如将内部消息格式转换为Anthropic Claude API所需的格式或者转换为本地Llama模型需要的提示词模板。工具Tool这是Kirara实现智能体Agent能力的关键。一个工具本质上是一个可以被AI模型调用的函数。AI模型在分析用户请求后如果判断需要调用外部能力如查询天气、计算、搜索网络它就会在回复中声明要调用某个工具并给出调用参数。Kirara的运行时捕获到这个声明后会执行对应的函数并将执行结果以tool角色的消息格式重新放回对话历史供AI模型生成最终的用户回复。例如你定义了一个get_current_weather的工具函数。当用户问“北京天气怎么样”时AI模型可能会回复一个结构化的调用请求“我需要调用get_current_weather工具参数是location’北京’。” Kirara框架会拦截这个请求执行真实的天气查询函数然后将查询结果“北京今天晴25度”作为信息补充给AIAI再组织成自然语言回复给用户“北京今天天气晴朗气温25摄氏度左右。”Kirara的强大之处在于它提供了一套优雅的装饰器或类机制让你能非常轻松地将一个普通的Python函数“包装”成一个AI可识别和调用的工具并自动处理参数的类型验证、文档生成描述这个工具是做什么的AI靠这个描述来决定是否调用以及调用流程。2.3 提供者Provider抽象统一的多模型接口AI世界是碎片化的OpenAI的GPT系列、Anthropic的Claude、Google的Gemini还有无数开源的Llama、Qwen等模型它们的API接口、参数命名、响应格式各有不同。如果你的应用代码里写满了针对不同API的适配代码将会是一场维护噩梦。Kirara通过提供者Provider抽象层解决了这个问题。它为“调用一个AI模型完成对话”这个操作定义了一个统一的接口。无论底层是OpenAI、Azure OpenAI、还是通过Ollama部署的本地模型在Kirara的上层业务逻辑看来它们都是一样的一个可以接收消息列表并返回AI回复的组件。当你需要切换模型时例如从昂贵的GPT-4降级到更经济的GPT-3.5-Turbo或者从云端模型切换到本地模型以保障数据隐私你只需要在配置中更改提供者的类型和参数而不需要重写任何核心的业务逻辑代码。这种设计极大地提升了应用的灵活性和未来兼容性。3. 快速上手构建你的第一个AI智能体理论说了这么多我们直接动手用Kirara快速搭建一个能查询天气和计算数学的简易智能体。这个过程会让你对Kirara的工作流有一个直观的感受。3.1 环境准备与安装首先确保你的Python环境在3.8以上。创建一个新的虚拟环境是一个好习惯。python -m venv kirara-env source kirara-env/bin/activate # Linux/macOS # 或 kirara-env\Scripts\activate # Windows然后安装Kirara。由于它可能处于活跃开发中我们通常直接从GitHub仓库安装最新版本。pip install githttps://github.com/lss233/kirara-ai.git此外我们还需要安装openai库因为我们的例子将使用OpenAI的模型。同时我们将用一个虚拟的天气查询工具来演示所以还会安装requests来模拟网络请求。pip install openai requests别忘了设置你的OpenAI API密钥。你可以将它设置为环境变量。export OPENAI_API_KEY你的-api-key-here注意在生产环境中切勿将API密钥硬编码在代码中。使用环境变量或安全的密钥管理服务是必须的。3.2 定义你的工具集工具是智能体的“手脚”。我们来定义两个简单的工具。工具一获取天气这是一个模拟工具它并不真正调用天气API而是返回一个固定的字符串。在实际应用中你可以在这里集成任何真实的API比如和风天气、OpenWeatherMap等。# tools.py import kirara from pydantic import BaseModel, Field # 首先定义工具的输入参数模型。这利用了Pydantic能提供强大的类型检查和数据验证。 # AI模型会参考这个模型的字段定义来生成调用参数。 class WeatherQueryInput(BaseModel): location: str Field(description城市名称例如北京、上海) unit: str Field(defaultcelsius, description温度单位celsius 或 fahrenheit) # 使用Kirara的装饰器 kirara.tool 来声明这是一个工具。 # 装饰器会自动提取函数的文档字符串docstring和参数模型生成给AI看的工具描述。 kirara.tool(args_schemaWeatherQueryInput) def get_current_weather(location: str, unit: str celsius) - str: 根据城市名称获取当前的天气信息。 # 模拟一个API调用和响应 # 真实场景下这里会是 requests.get(...) print(f[工具调用] 查询天气: 地点{location}, 单位{unit}) return f{location}的天气是晴朗气温22{unit}。 # 工具二计算器 class CalculatorInput(BaseModel): expression: str Field(description一个有效的数学表达式例如3 5 * 2) kirara.tool(args_schemaCalculatorInput) def calculate(expression: str) - str: 计算一个数学表达式的结果。 注意出于安全考虑在实际生产中应使用更安全的评估方法如 ast.literal_eval 或专用数学库。 print(f[工具调用] 计算表达式: {expression}) try: # 警告使用eval有安全风险仅用于演示。 # 在生产中务必替换为安全的计算方式 result eval(expression) return f表达式 {expression} 的计算结果是 {result}。 except Exception as e: return f计算表达式 {expression} 时出错{e}关键点解析Pydantic模型WeatherQueryInput和CalculatorInput类定义了工具所需的参数。Field中的description字段至关重要AI模型如GPT正是通过阅读这些描述来理解何时以及如何使用这个工具。装饰器kirara.tool这是Kirara的魔法所在。它自动将你的Python函数注册到框架中并为其生成符合OpenAI Function Calling或类似规范的元数据名称、描述、参数模式。安全警告示例中的calculate工具使用了eval()这在生产环境是极度危险的因为它会执行任意代码。这里仅作演示。正确的做法是使用ast.literal_eval仅支持常量表达式或像numexpr这样的安全计算库。3.3 配置与运行智能体现在我们将工具、模型和会话组合起来。# main.py import asyncio import kirara from kirara.providers import OpenAIProvider from tools import get_current_weather, calculate # 导入我们定义的工具 async def main(): # 1. 创建一个模型提供者这里使用OpenAI的gpt-3.5-turbo模型 # 你可以轻松替换为其他提供者如ClaudeProvider、OllamaProvider等。 provider OpenAIProvider( modelgpt-3.5-turbo, api_key你的-api-key # 更推荐从环境变量读取 ) # 2. 创建一个智能体Agent它是模型和工具的结合体。 # 我们将定义的工具列表传给它。 agent kirara.Agent( providerprovider, tools[get_current_weather, calculate], # 注册工具 system_message你是一个乐于助人的助手可以回答问题和使用工具。请根据用户需求在需要时主动使用你拥有的工具。, # 系统指令设定AI角色 ) # 3. 创建一个会话Session它代表一次独立的对话。 session agent.create_session() # 4. 开始对话循环 print(智能体已启动输入 退出 或 quit 来结束对话。) while True: try: user_input input(\n你: ) if user_input.lower() in [退出, quit, exit]: print(对话结束。) break # 使用会话的 run 方法处理用户输入。 # 这是一个异步方法它会 # a. 将用户输入追加到会话历史。 # b. 将整个历史包括可能的工具调用结果发送给AI模型。 # c. 如果AI返回工具调用请求则自动执行对应工具并将结果追加回历史。 # d. 重复b-c直到AI返回一个面向用户的文本回复。 # e. 流式输出最终回复。 print(助手: , end, flushTrue) async for chunk in session.run(user_input, streamTrue): # 流式输出逐个打印文本块实现打字机效果。 print(chunk, end, flushTrue) print() # 换行 except KeyboardInterrupt: print(\n对话被中断。) break except Exception as e: print(f\n发生错误: {e}) if __name__ __main__: asyncio.run(main())运行与交互 保存以上代码后运行python main.py。你会看到一个简单的命令行聊天界面。尝试输入以下内容“北京天气怎么样” - 观察控制台你会看到[工具调用]的日志然后AI会整合工具返回的结果给出回复。“计算一下3的平方加上4的平方等于多少” - AI会调用计算器工具。“先告诉我上海天气再计算(1020)/3的结果。” - 这是一个多轮工具调用的复杂请求Kirara的会话机制会自动管理整个流程。这个简单的例子展示了Kirara如何将模型调用、工具执行、会话管理这些繁琐的任务封装起来让你只需关注工具函数本身和业务逻辑。4. 深入核心会话管理与上下文工程4.1 会话的生命周期与状态管理在Kirara中Session对象是对话的核心容器。理解它的生命周期对于构建稳定应用至关重要。创建与初始化当你调用agent.create_session()时Kirara会创建一个新的、独立的会话对象。这个会话会继承智能体Agent的配置如系统消息、可用工具列表但拥有自己独立的对话历史内存。这意味着你可以用同一个Agent配置同时服务成千上万个用户每个用户的对话都是隔离的。消息历史的存储会话内部维护着一个消息列表。每次用户输入、AI回复、工具调用和工具返回结果都会被转换成特定格式的Message对象并按顺序追加到这个列表中。这个列表就是模型的“上下文”。Kirara会自动处理消息的格式转换以适配底层模型提供者的要求。上下文窗口与截断策略所有AI模型都有上下文长度限制如GPT-3.5-Turbo的16K Token。当对话历史越来越长超过模型限制时必须进行截断。Kirara内置了智能的截断策略。常见的策略包括丢弃最旧的对话这是最简单的方式但可能导致AI忘记很早之前的重要设定。优先保留系统消息和最近对话系统消息通常包含核心指令必须保留。同时保留最近几轮对话以保证连贯性。基于重要性的摘要更高级的策略是当历史过长时调用另一个AI模型对早期对话进行摘要然后将摘要作为一条新消息放入历史替代原有的大段内容。Kirara的架构允许你自定义这种ContextTruncator上下文截断器。会话的持久化对于Web应用会话需要被保存到数据库如Redis、PostgreSQL或文件中以便用户下次访问时能恢复对话。Kirara的Session对象通常可以被序列化如转换成JSON。你需要自己实现存储和加载的逻辑在用户连接时从存储中还原出会话对象。框架本身关注内存中的会话管理而持久化是应用层需要结合Web框架如FastAPI来实现的。4.2 系统消息System Prompt的设计艺术系统消息是塑造AI行为的“宪法”。一句好的系统提示词能让AI的输出质量有质的提升。在Kirara中你可以在创建Agent时通过system_message参数设置。基础角色设定agent kirara.Agent( providerprovider, system_message你是一个专业、友好且简洁的客服助手。你的主要职责是解答用户关于产品X的问题。如果遇到不知道的问题请如实告知并引导用户联系人工客服。不要编造信息。 )复杂指令与约束 对于更复杂的智能体你可能需要更详细的指令包括输出格式、禁止事项、推理步骤等。system_prompt 你是一个数据分析助手。请遵循以下规则 1. 当用户询问数据时首先思考需要调用哪个工具如 query_database, calculate_statistics。 2. 所有数值结果请保留两位小数。 3. 在回复的最后用‘---’分隔并附上一句对数据趋势的简短洞察。 4. 绝对不要对数据做出无法由工具结果支持的预测性结论。 动态系统消息有时系统消息需要根据会话上下文动态变化。例如在聊天过程中用户选择了“专家模式”你可能需要在系统消息中追加更专业的指令。Kirara允许你在会话层面覆盖或追加系统消息。你可以通过修改session.messages列表在最前面插入或更新一条role’system’的消息来实现。但要注意这可能会影响模型的上下文理解需谨慎操作。实操心得系统消息的调优是一个迭代过程。不要指望一次写完美。在实际测试中观察AI的“错误”回答思考是哪个指令没被遵守然后有针对性地修改系统提示词。一个技巧是在系统消息中明确要求AI“逐步思考”Think step by step这往往能提高其复杂任务上的表现。5. 高级特性工作流编排与自定义扩展当你的AI应用超越简单的“一问一答加工具调用”需要处理更复杂的、多步骤的、有条件分支的业务流程时就需要用到工作流Workflow编排。Kirara在这方面也提供了强大的支持。5.1 理解工作流Workflow工作流将一系列步骤Step组织成一个有向图。每个步骤可以是一个简单的AI调用、一个工具调用、一个条件判断甚至是另一个子工作流。步骤之间通过数据流连接前一个步骤的输出可以作为后一个步骤的输入。假设我们要构建一个“智能内容创作助手”工作流步骤A头脑风暴根据用户给的主题让AI生成5个文章标题。步骤B选择让用户从5个标题中手动选择一个这是一个需要人工干预的步骤。步骤C大纲生成根据选定的标题让AI生成详细的文章大纲。步骤D段落撰写根据大纲并行或依次让AI生成每个段落的内容。步骤E校对对生成的完整文章进行语法和风格校对。在Kirara中你可以用代码或声明式的方式来定义这样的工作流。框架会负责步骤的执行、状态的传递、错误的处理以及异步操作的协调。5.2 实现一个自定义工具集成外部知识库工具Tool是Kirara扩展性的基石。除了简单的函数工具可以非常复杂。让我们实现一个与外部向量数据库如Chroma、Pinecone集成的工具让AI具备检索私有知识的能力。import kirara from pydantic import BaseModel, Field from typing import List # 假设我们使用 sentence-transformers 进行文本嵌入使用 chromadb 作为向量库 from sentence_transformers import SentenceTransformer import chromadb class KnowledgeQueryInput(BaseModel): question: str Field(description用户提出的问题用于从知识库中检索相关信息) top_k: int Field(default3, description返回最相关的几条知识片段) kirara.tool(args_schemaKnowledgeQueryInput) def search_knowledge_base(question: str, top_k: int 3) - str: 从内部知识库中检索与问题最相关的文档片段。 返回检索到的文本内容供助手参考以回答问题。 # 1. 加载嵌入模型生产环境应缓存模型避免每次加载 embed_model SentenceTransformer(all-MiniLM-L6-v2) # 2. 将用户问题转换为向量 query_embedding embed_model.encode(question).tolist() # 3. 连接向量数据库 chroma_client chromadb.PersistentClient(path./chroma_db) collection chroma_client.get_or_create_collection(namecompany_docs) # 4. 执行相似度搜索 results collection.query( query_embeddings[query_embedding], n_resultstop_k ) # 5. 格式化检索结果 if results and results[documents]: retrieved_docs results[documents][0] formatted_context \n\n--- 参考知识 ---\n for i, doc in enumerate(retrieved_docs): formatted_context f[片段{i1}]: {doc}\n return formatted_context else: return [知识库检索] 未找到相关文档。 # 在创建Agent时将这个工具加入列表 agent kirara.Agent( providerprovider, tools[search_knowledge_base, get_current_weather, calculate], system_message你是一个拥有公司内部知识库访问权限的助手。当用户询问公司政策、产品细节或历史文档时请优先使用‘search_knowledge_base’工具获取准确信息然后基于检索到的信息进行回答。 )现在当用户询问“我们公司的年假政策是什么”时AI会先调用search_knowledge_base工具从向量数据库中检索出相关的员工手册片段然后基于这些确凿的信息生成回答避免了AI“胡编乱造”公司政策的情况。性能与优化注意嵌入模型缓存每次调用都加载模型极其低效。应在应用启动时全局加载一次或使用模型服务。数据库连接池对于高频应用需要管理数据库连接避免频繁创建和断开。超时与重试网络调用可能失败。工具函数内部应包含健壮的错误处理和重试逻辑并向AI返回清晰的错误信息例如“知识库暂时无法访问请稍后再试”。Token消耗检索到的文档可能很长全部放入上下文会消耗大量Token。可以考虑让工具本身集成一个摘要功能只返回最核心的几句话。5.3 自定义模型提供者Provider虽然Kirara内置了主流模型的提供者但你可能需要接入一个自定义的或更新的模型API。实现一个自定义提供者并不复杂关键是实现标准的接口。from typing import List, AsyncGenerator import kirara from kirara.models import Message class MyCustomLLMProvider(kirara.Provider): 一个自定义的本地模型提供者示例。 def __init__(self, model_path: str, api_base: str http://localhost:11434): self.model_path model_path self.api_base api_base async def chat_completion( self, messages: List[Message], stream: bool False, **kwargs ) - AsyncGenerator[str, None]: 核心方法接收消息历史调用模型返回回复。 需要处理流式和非流式两种模式。 # 1. 将Kirara的通用Message格式转换为你的模型API所需的格式 formatted_messages self._format_messages(messages) # 2. 准备API请求参数 payload { model: self.model_path, messages: formatted_messages, stream: stream, **kwargs # 传递其他参数如temperature, max_tokens等 } # 3. 发起请求这里以requests为例生产环境应用aiohttp import requests url f{self.api_base}/v1/chat/completions # 假设API兼容OpenAI格式 if stream: # 流式处理 with requests.post(url, jsonpayload, streamTrue) as response: response.raise_for_status() for line in response.iter_lines(): if line: # 解析流式返回的数据块提取delta content chunk self._parse_stream_chunk(line) if chunk: yield chunk else: # 非流式处理 response requests.post(url, jsonpayload) response.raise_for_status() result response.json() # 从result中提取完整的回复文本 full_content self._extract_content(result) # 对于非流式我们也可以包装成异步生成器只yield一次 yield full_content def _format_messages(self, messages: List[Message]) - list: # 实现格式转换逻辑例如转成Ollama、Anthropic等API要求的格式 formatted [] for msg in messages: # 这里需要根据目标API调整 formatted.append({role: msg.role, content: msg.content}) return formatted def _parse_stream_chunk(self, line: bytes) - str: # 解析流式响应行例如OpenAI格式是data: {...} # 这里需要根据目标API的流式响应格式来具体实现 # 伪代码解析JSON返回 choices[0].delta.content return parsed_chunk def _extract_content(self, result: dict) - str: # 从非流式响应结果中提取文本内容 # 伪代码返回 result[choices][0][message][content] return result.get(choices, [{}])[0].get(message, {}).get(content, )实现自定义提供者后你就可以像使用OpenAIProvider一样使用它my_provider MyCustomLLMProvider(model_pathqwen2.5:7b) agent kirara.Agent(providermy_provider, tools[...])这为Kirara接入了几乎无限的模型可能性无论是通过Ollama运行的本地模型还是企业内网的私有模型服务都可以轻松集成。6. 生产环境部署与性能调优将基于Kirara开发的原型应用部署到生产环境需要考虑一系列工程化问题。6.1 异步Async与并发处理Kirara的核心API设计是异步的使用async/await这是为了高效处理高并发的AI请求因为网络I/O调用模型API是主要的耗时操作。在Web服务器如FastAPI、Sanic中使用时务必确保你的请求处理函数也是异步的并正确awaitKirara的调用。from fastapi import FastAPI from pydantic import BaseModel app FastAPI() # 假设agent已在全局初始化 agent None # 实际应在启动时初始化 class ChatRequest(BaseModel): session_id: str message: str app.post(/chat) async def chat_endpoint(request: ChatRequest): # 1. 根据session_id从数据库或缓存中加载会话 session await load_session_from_db(request.session_id) if session is None: session agent.create_session() await save_session_to_db(request.session_id, session) # 2. 处理用户消息 full_response async for chunk in session.run(request.message, streamTrue): full_response chunk # 如果是真正的流式HTTP响应这里应该用Server-Sent Events (SSE)逐个发送chunk # await sse_queue.put(chunk) # 3. 保存更新后的会话 await save_session_to_db(request.session_id, session) # 4. 返回响应这里简化为例一次性返回 return {response: full_response}关键点会话存储load_session_from_db和save_session_to_db需要你自己实现可以使用Redis存储序列化的会话对象或数据库。流式响应对于更好的用户体验应该实现Server-Sent Events (SSE)来实时推送AI生成的每一个词。上述示例中的async for循环非常适合与SSE结合。全局AgentWeb应用中Agent实例包含模型配置和工具通常作为全局单例或依赖项初始化避免每次请求都重复创建。6.2 错误处理与重试机制生产环境中网络波动、模型API限流或临时故障是常态。你的应用必须具备韧性。工具调用重试在自定义工具函数内部对于可能失败的外部API调用如数据库查询、第三方服务应使用tenacity等库实现带退避策略的重试。from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def call_unstable_external_api(): # ...模型API调用重试Kirara的Provider层可能已经内置了部分重试逻辑。如果没有你可以在自定义Provider的chat_completion方法中实现或者使用一个包装器。OpenAI的官方库就有自动重试选项。优雅降级当核心工具或模型完全不可用时应有备用方案。例如知识库检索工具失败时可以配置AI直接回复“知识库暂时无法访问我将基于通用知识为您解答”并避免触发需要该工具的问题。6.3 监控、日志与成本控制结构化日志记录关键事件如会话创建、工具调用参数、结果、耗时、模型调用请求Token数、响应Token数、耗时。这有助于调试和成本分析。import logging import time logger logging.getLogger(__name__) kirara.tool async def some_tool(...): start_time time.time() # ... 工具逻辑 duration time.time() - start_time logger.info(fTool some_tool executed, extra{duration: duration, params: ...})Token消耗监控AI模型API的成本直接与Token消耗挂钩。Kirara内部可能会计算消息的Token数或依赖模型API返回的用量。你应该在每次对话后记录这些数据并设置告警防止因意外循环或攻击导致成本激增。性能剖析使用像cProfile或async-profiler这样的工具定期分析应用的性能瓶颈。常见瓶颈可能在于工具函数的网络延迟、向量数据库检索速度、大会话历史的Token计算等。针对性地进行优化比如为工具添加缓存、优化检索索引、实施更积极的会话摘要策略。7. 常见问题与排查技巧实录在实际开发和运维中你肯定会遇到各种问题。以下是一些典型场景及其解决思路。7.1 AI不调用工具现象你明明定义了工具并在Agent中注册了但AI在回答相关问题时完全无视工具只用自己的知识回答可能还会胡编乱造。排查步骤检查工具描述AI完全依赖你在kirara.tool装饰器中自动生成的函数文档字符串docstring和Field(description)来理解工具。确保描述清晰、准确说明了工具的用途、适用场景和参数含义。模糊的描述会导致AI无法正确匹配。检查系统消息系统消息中是否明确鼓励或要求AI使用工具例如加入“请优先使用你拥有的工具来获取准确信息”这样的指令。检查模型能力你使用的模型是否支持“函数调用”Function Calling或“工具使用”Tool Usegpt-3.5-turbo和gpt-4系列都支持。但一些更小、更老的模型或某些开源模型可能不支持。确保你的Provider配置的模型具备此能力。开启调试日志查看Kirara或底层模型库的调试输出看AI的回复中是否包含了工具调用的请求。有时AI可能以自然语言说“我将为你查询天气”但并没有输出结构化的工具调用请求。这需要你调整提示词。简化测试用一个极其明确、工具唯一能解决的问题测试如“请使用计算器工具计算12345乘以67890”。如果这都不调用那问题肯定出在配置或模型上。7.2 流式输出中断或不流畅现象在Web前端使用SSE接收流式响应时经常中断或者内容是一大段一起出来没有“打字机”效果。排查步骤检查网络与代理确保服务器到客户端之间没有代理或防火墙对流式响应text/event-stream进行缓冲或干扰。一些云服务商的负载均衡器默认配置可能会缓冲响应。检查服务器端生成速度在服务器端代码中检查async for chunk in session.run(...)循环内chunk的大小。如果模型API返回的块本身就很大比如一整句话那么流式效果就不明显。同时确保在yield每个chunk后立即刷新flush。前端SSE实现检查前端EventSource的实现是否正确处理了onmessage事件并是否在连接断开时尝试重连。超时设置服务器和客户端的读写超时时间要设置得足够长。AI生成一段长文本可能需要数十秒任何一方的超时设置过短都会导致连接断开。7.3 会话历史过长导致响应慢或API错误现象随着对话轮数增加应用响应越来越慢最终可能收到模型API返回的“上下文长度超限”错误。解决方案启用自动截断确认你使用的Kirara版本或Provider配置是否开启了自动上下文截断。查看相关配置项如max_tokens、max_context_length。实现自定义截断策略如果内置策略不满足需求例如你需要永久记住用户的某个关键信息可以实现一个自定义的ContextTruncator。例如在截断时永远保留第一条系统消息和最近N条用户/助手对话同时如果历史中包含标记为“重要”的消息也予以保留。主动进行会话摘要在对话达到一定长度后可以主动触发一个“摘要”步骤。调用另一个AI模型如更便宜、更快的模型将早期的对话历史总结成一段简洁的文字然后用这段摘要替换掉大段旧历史。Kirara的工作流功能非常适合编排这个自动化过程。优化工具返回内容工具函数应返回精炼、相关的信息避免返回冗长的HTML、JSON原始数据或错误堆栈。在工具内部就对数据进行清洗和摘要。7.4 工具函数执行慢阻塞整个会话现象某个工具如调用一个慢速的外部API执行需要10秒钟导致整个聊天响应卡住。解决方案异步工具将工具函数定义为async def并在其内部使用异步HTTP客户端如aiohttp进行网络调用。这样在等待外部API响应时事件循环可以处理其他任务。kirara.tool async def slow_network_tool(...): async with aiohttp.ClientSession() as session: async with session.get(slow_api_url) as response: data await response.json() return process(data)设置超时为工具调用设置超时。可以使用asyncio.wait_for来包装工具调用如果超时则向AI返回一个超时提示而不是让用户无限等待。try: result await asyncio.wait_for(slow_network_tool(...), timeout5.0) except asyncio.TimeoutError: return “请求外部服务超时请稍后重试。”缓存结果对于幂等的、结果变化不频繁的工具调用如根据城市查询天气结果在短时间内是相同的可以添加缓存层如使用functools.lru_cache或Redis在缓存有效期内直接返回缓存结果极大提升响应速度。开发基于Kirara的AI应用是一个将创意快速落地的愉快过程。它通过精心的抽象把脏活累活都揽了过去让你能聚焦在构建有价值的工具和设计巧妙的交互流程上。从简单的问答机器人到复杂的多智能体协作系统这个框架都能提供坚实的支撑。当然就像任何框架一样深入使用后你可能会遇到需要定制和绕过的场景但它的设计总体上足够灵活能够容纳这些高级需求。