企业AI助理系统采用分层架构设计,包含智能体交互、知识中枢(RAG)、数据分析(NL2SQL)和工具调用(MCP)三大核心功能模块,支持自然语言查询、跨系统操作和可视化报表生成。技术选型上选用ASP.NET Core后端框架、Semantic Kernel AI框架、Qdrant向量数据库,并支持私有化部署。系统通过权限控制确保安全性,采用容器化云原生部署方案,为企业提供覆盖现有系统的智能化交互层

 AI开发01 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post

https://blog.csdn.net/cao919/article/details/155895060

上个专题完善的docker问题 以及最后完善的docker遇到的问题

其中在docker desktop 中以成功
docker pull docker.m.daocloud.io/postgres:15-alpine
docker tag docker.m.daocloud.io/postgres:15-alpine postgres:15-alpine

手机wifi
docker pull sosedoff/pgweb:latest
docker tag sosedoff/pgweb:latest sosedoff/pgweb:latest  

docker volume rm postgres-aicopilot
Error response from daemon: remove postgres-aicopilot: volume is in use - [d18050b549f265821aeb97ad6ba58ca7593951f2c7c717ba739840f1da6cc7bf, ad4152ddc46e835017d74bd440d4e74c66c605e8330453d44dad4088b3544c
a7]
docker ps -a --filter volume=postgres-aicopilot
docker stop d18050b549f2
docker rm d18050b549f2
docker ps -a --filter volume=postgres-aicopilot
docker volume rm postgres-aicopilot

第五章  技术关键词

Senmantic Kernel


AutoGen

Microsoft Agent Framework =MAF

SK的底层+AutoGen编排


1.显示的工作流编排
2.强大的状态管理
3.开放互操作性:MCP+A2A


SK=引擎==》集成层==连接器

AutoGen=概念车==》研究探索==》对话编排

Agent框架=量产车==》企业就智能体平台==》可观测、调试、一致性

多轮对话以及记忆管理
记忆:
1.短期记忆: 聊天历史==》只通过上下文窗口传递的内容,容量有限

2.长期记忆: 能够提取、存储有意义的信息,其他的对话中也可以使用,和会话无关,可以跨多个会话
内存存储 (Agent线程对象)==》Agent线程:用来保存会话状态底层服务(语言模型)
1111
6666
工作流
6666

AI Copilot网关服务重构:支持多模型与流式对话

- ConversationTemplate 增加 ModelId 字段,支持多模型绑定,领域模型与数据库结构同步调整
- TemplateSpecification/ModelParameters 的 Temperature 字段由 double 改为 float,数据库类型同步为 real
- CQRS 用例层重构,命名空间与文件结构优化,DTO 结构规范化,权限控制统一
- 新增 ChatAgentFactory、SessionChatMessageStore,集成 OpenAI Agent 框架,支持流式消息输出(SSE)
- AiGatewayController 新增流式对话接口,API 层风格统一
- 依赖注入与项目引用优化,所有服务共用 Common.Contracts,移除冗余 Contracts 项目
- 数据库迁移文件重生成,表结构、索引、字段类型与新模型一致
- 统一异常与权限相关代码风格,删除无用代码,提升整体可维护性

{

    "provider": "qwen-plus",

    "name": "通义千问fstyle",

    "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",

    "apiKey": "sk-",

    "maxTokens": "1000000",

    "temperature": 0.7

}

{

    "id": "a4c5e567-7424-4c4e-8d2a-15f4b1c46dbd",

    "provider": "qwen-plus",

    "name": "通义千问fstyle"

}

{

    "name": "AI助手",

    "description": "测试用的AI助手",

    "systemPrompt": "你是一个乐于助人的AI助手,你的名字叫音娱乐行",

    "modelId": "a4c5e567-7424-4c4e-8d2a-15f4b1c46dbd",

    "maxTokens": "256000",

    "temperature": "0.7"

}

{

    "id": "7a75a9c3-b8b1-4ec9-914e-be72942d4294",

    "name": "AI助手"

}

{

    "templateId": "7a75a9c3-b8b1-4ec9-914e-be72942d4294"

}

{

    "id": "62c4910e-64a8-4c97-9750-ca55f0b4cd04"

}

{

    "sessionId": "62c4910e-64a8-4c97-9750-ca55f0b4cd04",

    "content": "你好!你是?"

}

--name居然是模型名称QAQ

效果

$.content

历史消息

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Zilor.AICopilot.Core.AiGateway.Aggregates.Sessions;
using Zilor.AICopilot.Services.Common.Contracts;
using Zilor.AICopilot.SharedKernel.Repository;

namespace Zilor.AICopilot.AiGatewayService.Agents;

public class SessionChatMessageStore : ChatMessageStore
{
    private readonly IServiceProvider _serviceProvider;

    public SessionChatMessageStore(IServiceProvider serviceProvider, JsonElement storeState)
    {
        _serviceProvider = serviceProvider;
        if (storeState.ValueKind is JsonValueKind.String)
        {
            ThreadDbKey = storeState.Deserialize<Guid>();
        }
    }

    private Guid? ThreadDbKey { get; set; }
    
    public override async Task<IEnumerable<ChatMessage>> GetMessagesAsync(CancellationToken cancellationToken = new())
    {
        using var scope = _serviceProvider.CreateScope();
        var queryService = scope.ServiceProvider.GetRequiredService<IDataQueryService>();

        // 先按时间倒序(Descending)取最新的 50 条
        var queryable = queryService.Messages
            .Where(m => m.SessionId == ThreadDbKey)
            .OrderByDescending(m => m.CreatedAt)
            .Take(50);
        
        var dbMessages = await queryService.ToListAsync(queryable); 
        
        // 在内存中反转回正序(Ascending),因为语言需要按时间顺序阅读
    	var orderedMessages = dbMessages.OrderBy(m => m.CreatedAt);
        
        // 将实体转换为 Agent 框架的 ChatMessage
        var chatMessages = new List<ChatMessage>();
        
        foreach (var msg in orderedMessages)
        {
            var role = msg.Type switch
            {
                MessageType.User => ChatRole.User,
                MessageType.Assistant =>  ChatRole.Assistant,
                MessageType.System => ChatRole.System,
                _ => ChatRole.User
            };
            chatMessages.Add(new ChatMessage(role, msg.Content));
        }

        return chatMessages;
    }

    public override async Task AddMessagesAsync(IEnumerable<ChatMessage> messages, CancellationToken cancellationToken = new())
    {
        ThreadDbKey ??= Guid.NewGuid();
        using var scope = _serviceProvider.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IRepository<Session>>();

        // 加载聚合根
        var session = await repo.GetByIdAsync(ThreadDbKey, cancellationToken);
        if (session == null) return;

        var hasNewMessage = false;
        foreach (var msg in messages)
        {
            // 将 Agent Role 转换为枚举
            // msg.Role.ToString() 可能返回 "user", "assistant" 等
            var roleStr = msg.Role.ToString().ToLower(); 
            var msgType = roleStr switch
            {
                "user" => MessageType.User,
                "assistant" => MessageType.Assistant,
                "system" => MessageType.System,
                _ => MessageType.Assistant
            };

            // 获取文本内容
            if (string.IsNullOrWhiteSpace(msg.Text)) continue;
            
            session.AddMessage(msg.Text, msgType);
            hasNewMessage = true;
        }

        if (hasNewMessage)
        {
            repo.Update(session);
            await repo.SaveChangesAsync(cancellationToken);
        }
    }

    public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
    {
        return JsonSerializer.SerializeToElement(ThreadDbKey);
    }
}

openAI 连接池优化 

第六章  技术关键词

引入Agent插件机制,支持AI工具动态扩展

本次提交实现了Agent插件基础设施(AICopilot.AgentPlugin),支持AI工具(如时间查询)的自动发现、注册与运行时动态调用。AiGatewayService集成插件机制,示例实现TimePlugin,Agent可在对话中调用C#工具方法。升级EF Core、Aspire、OpenTelemetry等依赖,修正数据库迁移与字段映射,提升系统可维护性与可观测性。删除无用接口,优化部分模型构造。
 

Agent: 不会行动==》行动能力==》函数调用
函数调用==》LLM根据用户的意图,智能决定是否需要调用外部函数,并生成该函数所需的参数
用户==》服务员
LLM (Agent):服务员(菜单定义)==》
下单==》菜名、数量==》经理(程序) ==》吩咐做菜(执行代码)==》传递给服务员==》最终回复
函数:后厨和菜单
应用(程序):经理
1.解决实时性和封闭性问题
2.实现结构化数据输出
3. 减少幻觉
Agent框架:
1.定义工具+描述
2.注册工具: 挂载给Agent
工具、函数、插件
2023年初==》OpenAl ChatGPT 插件
函数:原子==》方法
插件: 分子==》一组相关函数的集合==》类
工具:函数(代码)==》工具(LLM)

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Zilor.AICopilot.AgentPlugin;
using Zilor.AICopilot.AiGatewayService.Agents;
using Zilor.AICopilot.AiGatewayService.Plugins;
using Zilor.AICopilot.Core.AiGateway.Aggregates.Sessions;
using Zilor.AICopilot.Services.Common.Attributes;
using Zilor.AICopilot.SharedKernel.Messaging;
using Zilor.AICopilot.SharedKernel.Repository;

namespace Zilor.AICopilot.AiGatewayService.Commands.Sessions;

[AuthorizeRequirement("AiGateway.SendUserMessage")]
public record SendUserMessageCommand(Guid SessionId, string Content) : ICommand<IAsyncEnumerable<string>>;

public class SendUserMessageCommandHandler(
    IRepository<Session> repo,
    ChatAgentFactory chatAgent,
    AgentPluginLoader pluginLoader)
    : ICommandHandler<SendUserMessageCommand, IAsyncEnumerable<string>>
{
    public async Task<IAsyncEnumerable<string>> Handle(SendUserMessageCommand request, CancellationToken cancellationToken)
    {
        var session = await repo.GetByIdAsync(request.SessionId, cancellationToken);
        if (session == null) throw new Exception("未找到会话");
        
        var agent = await chatAgent.CreateAgentAsync(session.TemplateId);
        var storeThread = new { storeState = request.SessionId };
        var agentThread = agent.DeserializeThread(JsonSerializer.SerializeToElement(storeThread));
        
        // 返回迭代器函数
        return await Task.FromResult(GetStreamAsync(agent, agentThread, request.Content, cancellationToken));
    }

    private async IAsyncEnumerable<string> GetStreamAsync(
        ChatClientAgent agent, AgentThread thread, string input, [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        var tools = pluginLoader.GetAITools(nameof(TimePlugin));
        
        // 调用Agent流式读取响应
        await foreach (var update in agent.RunStreamingAsync(input, thread,
                           new ChatClientAgentRunOptions()
                           {
                               ChatOptions = new ChatOptions()
                               {
                                   Tools = tools
                               }
                           },
                           cancellationToken: cancellationToken))
        {
            foreach (var content in update.Contents)
            {
                switch (content)
                {
                    case TextContent callContent:
                        yield return callContent.Text;
                        break;
                    case FunctionCallContent callContent:
                        yield return $"\n\n```\n正在执行工具:{callContent.Name} \n请求参数:{JsonSerializer.Serialize(callContent.Arguments)}";
                        break;
                    case FunctionResultContent callContent:
                        yield return $"\n\n执行结果:{JsonSerializer.Serialize(callContent.Result)}\n```\n\n";
                        break;
                }
            }
        }
    }
}


Agent 框架
1111

可观测性:日志、指标、链路追踪
透视思考过程、调试工具调用、性能分析
Aspire+OpenTelemtry

// 1. 获取服务名称,这决定了在仪表板 里看到的名字
var serviceName = Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? nameof(Zilor.AICopilot.HttpApi);
// 2. 获取数据上报地址,Aspire 会自动注入这个环境变量
var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4317";

var resource = ResourceBuilder.CreateDefault()
    .AddService(serviceName);

// 3. 构建追踪提供程序
Sdk.CreateTracerProviderBuilder()
    .SetResourceBuilder(resource)
    // 核心配置:监听 Agent 框架发出的信号
    .AddSource(nameof(Zilor.AICopilot.AiGatewayService))     // 监听我们自己业务代码的 Activity
    .AddSource("*Microsoft.Agents.AI")       // 监听 Agent Framework 的核心事件
    .AddSource("*Microsoft.Extensions.AI*")   // 监听底层 AI 扩展库的事件
    .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint)) // 将数据导出到 Aspire Dashboard
    .Build();


1111
如何管理工具?
工具装配策略?什么时间、什么地点、什么方式,将哪些工具装配给Agent?
静态单例装配
动态按需加载
混合策略
HR==》人力资源:查询年假、提交请假单、撤销请假单..…
1.冷启动开销为0
2.资源节约
3.确定和稳定


企业级超级助理
瑞士军刀陷阱:
1.Token浪费
2.上下文挤压
3.选择困难症与幻觉


QueryOrderList==》SearchOrdersByDate
误调用、幻觉、拒绝服务
解耦和懒加载==》动态按需加载


管家(Agent)
元认知的环节==》意图识别--》工具筛选--》及时组装--》执行销毁
轻量级分类器:便宜、更快的小模型==》做选择题
向量语义检索:工具描述向量化存储到向量数据库,用户查询也先向量==》相似度搜索==》找出匹配的工具包
财务报表/邮件服务


工具筛选:只加载相关的插件(报表插件、Email插件)
即时组装:创建Agent/执行Agent挂载筛选过的工具
执行与销毁


极高的精准度、Token经济、无限的水平扩展能力
延迟增加、架构复杂度
混合策略:内核+插件
通用工具常驻
专业工具按需加载
意图识别Agent
1111
Agent 插件系统==》函数调用功能AI扩展库提供
约定大于配置==》利用.NET反射机制+函数描述特性
模块化解耦==》插件打包在不同的程序集、实现物理隔离
动态按需加载==》插件加载器

using System.ComponentModel;
using System.Reflection;
using Microsoft.Extensions.AI;

namespace Zilor.AICopilot.AgentPlugin;

public abstract class AgentPluginBase : IAgentPlugin
{
    // 默认实现:直接使用类名作为插件名称
    public virtual string Name { get; } 
    
    public virtual string Description { get; protected set; } = string.Empty;

    protected AgentPluginBase()
    {
        Name = GetType().Name;
    }

    /// <summary>
    /// 核心逻辑:扫描当前类中所有标记了 [Description] 的公共方法。
    /// 只有带有描述的方法才会被视为 AI 工具。
    /// </summary>
    private IEnumerable<MethodInfo> GetToolMethods()
    {
        var type = GetType();
        return type.GetMethods(BindingFlags.Instance | BindingFlags.Public)      
            .Where(m => m.GetCustomAttribute<DescriptionAttribute>() != null);
    }
    
    /// <summary>
    /// 利用 Microsoft.Extensions.AI 库,将 C# 方法自动转换为 AITool。
    /// </summary>
    public IEnumerable<AITool>? GetAITools()
    {
        // AIFunctionFactory.Create 是微软提供的工具,
        // 它会读取方法签名、参数类型和 Description 特性,生成 JSON Schema。
        // 'this' 参数确保了当工具被调用时,是在当前插件实例上执行的。
        var tools = GetToolMethods()
            .Select(method => AIFunctionFactory.Create(method, this));
        return tools;
    }
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Zilor.AICopilot.AgentPlugin;

public static class AgentPluginExtensions
{
    public static IServiceCollection AddAgentPlugin(
        this IServiceCollection services, 
        Action<IAgentPluginRegistrar> configure)
    {
        // 1. 创建注册器实例
        var registrar = new AgentPluginRegistrar();

        // 2. 执行用户配置(用户在这里指定要扫描的程序集)
        configure(registrar);

        // 3. 将注册器注册为单例。注意这里没有使用 TryAdd,
        // 因为我们允许用户多次调用 AddAgentPlugin 来注册不同来源的插件。
        services.AddSingleton<IAgentPluginRegistrar>(registrar);

        // 4. 注册加载器。加载器只应有一个,它会收集容器中所有的 Registrar。
        services.TryAddSingleton<AgentPluginLoader>();

        return services;
    }
}
using System.Reflection;
using Microsoft.Extensions.AI;

namespace Zilor.AICopilot.AgentPlugin;

public class AgentPluginLoader
{
    // 缓存插件实例:Key=插件名, Value=插件实例
    private readonly Dictionary<string, IAgentPlugin> _plugins = new();
    
    // 缓存工具定义:Key=插件名, Value=AITool数组
    private readonly Dictionary<string, AITool[]> _aiTools = new();
    
    // 构造函数注入所有的注册器
    public AgentPluginLoader(IEnumerable<IAgentPluginRegistrar> registrars)
    {
        // 1. 汇总所有需要扫描的程序集,去重
        var assemblies = registrars
            .SelectMany(r => r.Assemblies)
            .Distinct()
            .ToList();

        // 2. 扫描并加载
        foreach (var assembly in assemblies)
        {
            LoadPluginsFromAssembly(assembly);
        }
    }
    
    private void LoadPluginsFromAssembly(Assembly assembly)
    {
        // 反射查找:实现了 IAgentPlugin 且不是抽象类的具体类
        var pluginTypes = assembly.GetTypes()
            .Where(t =>
                typeof(IAgentPlugin).IsAssignableFrom(t) &&
                t is { IsClass: true, IsAbstract: false });

        foreach (var type in pluginTypes)
        {
            // 创建实例
            var plugin = (IAgentPlugin)Activator.CreateInstance(type)!;
            
            // 存入缓存
            _plugins[plugin.Name] = plugin;
            _aiTools[plugin.Name] = plugin.GetAITools()?.ToArray() ?? [];
        }
    }
    
    /// <summary>
    /// 核心功能:根据名称动态获取工具集。
    /// 支持一次获取多个插件的工具,实现工具的动态混搭。
    /// </summary>
    public AITool[] GetAITools(params string[] names)
    {
        var aiTools = new List<AITool>();
        foreach (var name in names)
        {
            if (_aiTools.TryGetValue(name, out var tools))
            {
                aiTools.AddRange(tools);
            }
        }
        return aiTools.ToArray();
    }
    
    public IAgentPlugin? GetPlugin(string name)
    {
        _plugins.TryGetValue(name, out var plugin);
        return plugin;
    }
    
    public IAgentPlugin[] GetAllPlugin()
    {
        return _plugins.Values.ToArray();
    }
}

效果

docker遇到的问题

docker pull docker.m.daocloud.io/sosedoff/pgweb:latest Error response from daemon: pull access denied for docker.m.daocloud.io/sosedoff/pgweb, repository does not exist or may require 'docker login': denied: 🚫 👀-> github.com/DaoCloud/public-image-mirror/issues/2328 🔗 这 镜像不在白名单. this image is not in the allowlist.

这个错误是因为 DaoCloud 镜像源未收录 pgweb 镜像(白名单限制),无需纠结该源,直接换用「手动导入 pgweb 镜像」或「阿里云专属源(若已配置)」解决,以下是 Windows 环境下的实操步骤:

方案一:手动下载 + 导入 pgweb 镜像(最快生效,无需依赖镜像源)

步骤 1:下载 pgweb 镜像包(PowerShell 执行)

powershell

# 下载 pgweb 镜像包到 C 盘根目录(可修改路径)
Invoke-WebRequest -Uri "https://github.com/liutianyi1996/docker-image/raw/main/pgweb/pgweb-latest.tar.gz" -OutFile "C:\pgweb-latest.tar.gz"

若 GitHub 下载慢,可改用国内wp链接(复制到浏览器下载): 

步骤 2:解压并导入 Docker(PowerShell 执行)

powershell

# 解压镜像包(需提前安装解压工具,或手动右键解压)
# 若用 7-Zip,执行:& "C:\Program Files\7-Zip\7z.exe" x C:\pgweb-latest.tar.gz -oC:\
# 手动解压后得到 pgweb-latest.tar,执行导入:
docker load < C:\pgweb-latest.tar

# 验证导入结果
docker images | Select-String "pgweb"

正常输出示例:sosedoff/pgweb latest xxxxxxxx 2 hours ago 25MB

方案二:用官方源 + 代理 / 热点绕过限制(若网络允许)

如果你的网络切换到手机热点后能访问 Docker Hub,直接拉取官方 pgweb 镜像:

powershell

# 切换手机热点后执行
docker pull sosedoff/pgweb:latest

# 验证
docker images | Select-String "pgweb"

方案三:阿里云专属源(若已配置)

若你已获取阿里云专属镜像加速地址(推荐),配置后直接拉取:

  1. 确认 Docker Engine 中已配置阿里云源(参考之前步骤);
  2. PowerShell 执行:

    powershell

    docker pull sosedoff/pgweb:latest
    

最终验证:确保所有依赖镜像就绪

执行以下命令,确认 postgres 和 pgweb 镜像都存在:

powershell

# 查看 postgres 镜像
docker images | Select-String "postgres"

# 查看 pgweb 镜像
docker images | Select-String "pgweb"

只要两者都有输出,说明你的 C# 代码所需的镜像全部就绪,运行 dotnet run 即可正常创建 PostgreSQL + pgWeb 容器,无需额外配置。

补充说明

pgweb 属于小众镜像,多数公共镜像源未收录,手动导入是最稳定的方式。导入后,你的代码中 .WithPgWeb(pgWeb => pgWeb.WithHostPort(5050)) 会自动识别本地的 pgweb 镜像,正常映射 5050 端口,无需修改代码。

using Projects; var builder = DistributedApplication.CreateBuilder(args); var postgresdb = builder.AddPostgres("postgres") .WithDataVolume("postgres-aicopilot") .WithPgWeb(pgAdmin => pgAdmin.WithHostPort(5050)) .AddDatabase("ai-copilot"); var rabbitmq = builder.AddRabbitMQ("eventbus") .WithManagementPlugin() .WithLifetime(ContainerLifetime.Persistent); var qdrant = builder.AddQdrant("qdrant") .WithLifetime(ContainerLifetime.Persistent); var migration = builder.AddProject<Zilor_AICopilot_MigrationWorkApp>("aicopilot-migration") .WithReference(postgresdb) .WaitFor(postgresdb); builder.AddProject<Zilor_AICopilot_HttpApi>("aicopilot-httpapi") .WithUrl("swagger") .WaitFor(postgresdb) .WaitFor(rabbitmq) .WaitFor(qdrant) .WithReference(postgresdb) .WithReference(rabbitmq) .WithReference(migration) .WithReference(qdrant) .WaitForCompletion(migration); builder.AddProject<Zilor_AICopilot_RagWorker>("rag-worker") .WithReference(postgresdb) // 注入数据库连接 .WithReference(rabbitmq) // 注入 RabbitMQ 连接 .WithReference(qdrant) .WaitFor(postgresdb) // 等待数据库启动 .WaitFor(rabbitmq) 等待 MQ 启动 . WaitFor(qdrant); builder. Build(). Run(); 其中在docker desktop 中以成功 docker pull docker.m.daocloud.io/postgres:15-alpine docker tag docker.m.daocloud.io/postgres:15-alpine postgres:15-alpine docker pull sosedoff/pgweb:latest docker tag sosedoff/pgweb:latest sosedoff/pgweb:latest 补充其他的

你已经成功拉取了 PostgreSQL 和 pgWeb 镜像,现在需要补充代码中用到的 RabbitMQ 和 Qdrant 镜像(这两个是代码里 AddRabbitMQ/AddQdrant 依赖的核心镜像),同时修复代码语法错误,确保整体能正常运行。以下是完整的操作步骤:

一、第一步:拉取 RabbitMQ 和 Qdrant 镜像(Docker 命令)

在 Windows PowerShell 中执行以下命令,拉取代码依赖的所有镜像(适配国内网络):

1. 拉取 RabbitMQ 镜像(带管理插件,匹配代码 WithManagementPlugin()

powershell

# 拉取带管理插件的 RabbitMQ 镜像(3.13-alpine 轻量化版本)
docker pull docker.m.daocloud.io/rabbitmq:3.13-management-alpine

# 打官方标签,适配 .NET Aspire 识别
docker tag docker.m.daocloud.io/rabbitmq:3.13-management-alpine rabbitmq:3.13-management-alpine

# (备用)若 DaoCloud 源失效,用官方源(切换手机热点)
# docker pull rabbitmq:3.13-management-alpine
2. 拉取 Qdrant 镜像(匹配代码 AddQdrant("qdrant")

powershell

# 拉取 Qdrant 镜像(v1.11 稳定版)
docker pull docker.m.daocloud.io/qdrant/qdrant:v1.11.3

# 打官方标签
docker tag docker.m.daocloud.io/qdrant/qdrant:v1.11.3 qdrant/qdrant:v1.11.3

# (备用)官方源
# docker pull qdrant/qdrant:v1.11.3
3. 验证所有镜像是否就绪(Windows PowerShell 命令)

powershell

# 查看所有依赖镜像(postgres/pgweb/rabbitmq/qdrant)
docker images | Select-String -Pattern "postgres|pgweb|rabbitmq|qdrant"

正常输出示例:

plaintext

postgres               15-alpine          xxxxxxxx    1 hour ago    194MB
sosedoff/pgweb         latest             xxxxxxxx    1 hour ago    25MB
rabbitmq               3.13-management-alpine xxxxxxxx  1 hour ago    220MB
qdrant/qdrant          v1.11.3            xxxxxxxx    1 hour ago    450MB

二、第二步:修复代码中的语法错误(关键!)

你的代码有 2 处语法错误,运行时会直接报错,先修正:

csharp

运行

using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var postgresdb = builder.AddPostgres("postgres")
    .WithDataVolume("postgres-aicopilot")
    .WithPgWeb(pgAdmin => pgAdmin.WithHostPort(5050))
    .AddDatabase("ai-copilot");

var rabbitmq = builder.AddRabbitMQ("eventbus")
    .WithManagementPlugin()
    .WithLifetime(ContainerLifetime.Persistent);

var qdrant = builder.AddQdrant("qdrant")
    .WithLifetime(ContainerLifetime.Persistent);

var migration = builder.AddProject<Zilor_AICopilot_MigrationWorkApp>("aicopilot-migration")
    .WithReference(postgresdb)
    .WaitFor(postgresdb);

builder.AddProject<Zilor_AICopilot_HttpApi>("aicopilot-httpapi")
    .WithUrl("swagger")
    .WaitFor(postgresdb)
    .WaitFor(rabbitmq)
    .WaitFor(qdrant)
    .WithReference(postgresdb)
    .WithReference(rabbitmq)
    .WithReference(migration)
    .WithReference(qdrant)
    .WaitForCompletion(migration);

builder.AddProject<Zilor_AICopilot_RagWorker>("rag-worker")
    .WithReference(postgresdb) // 注入数据库连接
    .WithReference(rabbitmq)   // 注入 RabbitMQ 连接
    .WithReference(qdrant)
    .WaitFor(postgresdb)       // 等待数据库启动
    .WaitFor(rabbitmq)         // 修复:缺少注释符 + 空格错误
    .WaitFor(qdrant);          // 修复:多余的空格

builder.Build().Run();  // 修复:Build() 和 Run() 前多余的空格

三、第三步:运行 .NET Aspire 项目(核心步骤)

  1. 确保 Docker Desktop 处于运行状态(右下角显示 Docker Desktop running);
  2. 打开项目根目录的 PowerShell,执行以下命令:

    powershell

    # 1. 安装/更新 .NET Aspire 工作负载(确保依赖完整)
    dotnet workload install aspire
    
    # 2. 还原项目依赖
    dotnet restore
    
    # 3. 运行 Aspire 项目(自动创建所有容器/卷/网络)
    dotnet run
    

四、关键补充说明

1. 容器生命周期与数据持久化

代码中 WithLifetime(ContainerLifetime.Persistent) 表示容器会持久化运行(不会随项目停止而删除),数据卷会保留:

  • PostgreSQL 数据卷:postgres-aicopilot(已手动创建);
  • RabbitMQ/Qdrant 数据卷:Aspire 会自动创建,无需手动操作。
2. 端口访问说明
  • pgWeb(PostgreSQL 管理):http://localhost:5050(代码中 WithHostPort(5050));
  • RabbitMQ 管理面板:默认端口 15672(WithManagementPlugin() 自动映射,访问 http://localhost:15672,默认账号 / 密码:guest/guest);
  • Qdrant 控制台:默认端口 6333(可通过 WithHostPort(6333) 显式映射,可选)。
3. 常见问题排查
  • 容器启动失败

    powershell

    # 查看所有容器状态
    docker ps -a
    
    # 查看报错容器日志(替换 <容器名/ID>)
    docker logs <容器名/ID>
    
  • 端口占用

    powershell

    # 检查 5050/15672/6333 端口是否被占用
    netstat -ano | findstr ":5050"
    netstat -ano | findstr ":15672"
    netstat -ano | findstr ":6333"
    
    # 杀死占用进程(替换 <PID>)
    taskkill /F /PID <PID>
    
  • 迁移项目等待超时:确保 Zilor_AICopilot_MigrationWorkApp 项目能正常连接 PostgreSQL,可先单独运行迁移项目验证。

五、最终验证

运行 dotnet run 后,Aspire 会启动一个 Dashboard(默认地址 http://localhost:18888),在面板中可看到:

  • postgres 容器状态为「Running」;
  • eventbus(RabbitMQ)容器状态为「Running」;
  • qdrant 容器状态为「Running」;
  • 迁移项目 aicopilot-migration 执行完成;
  • aicopilot-httpapi 和 rag-worker 正常启动。

至此,代码中所有依赖的 Docker 镜像已全部准备完毕,语法错误已修复,项目可正常运行。

其他专题

AI开发 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post

https://blog.csdn.net/cao919/article/details/155895060

.net AI MCP 入门 适用于模型上下文协议的 C# SDK 简介(MCP)
https://blog.csdn.net/cao919/article/details/147915384

C# .net ai Agent AI视觉应用 写代码 改作业 识别屏幕 简历处理 标注等
https://blog.csdn.net/cao919/article/details/146504537

C# net deepseek RAG AI开发 全流程 介绍
https://blog.csdn.net/cao919/article/details/147915384

WPF halcon 机器视觉
https://blog.csdn.net/cao919/article/details/134790240 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐