Agent 设计模式系列

Agent 设计模式 5:条件工作流:面试与拒绝分支

根据评分选择安排面试或发送拒绝通知,并阅读 mock 业务工具与 RAG 上下文的接入。

评审完成后,需要根据结果选择下一步:通过则安排面试,未通过则发送拒绝通知。本讲用两个分支演示条件工作流;邮件、日程和状态更新工具均沿用原文的 mock 实现。

一、定义不同分支的Agent

1.1 EmailAssistant (发邮件拒绝候选人Agent)

public interface EmailAssistant {

    @Agent("向未通过筛选的候选人发送拒绝邮件,返回已发送邮件的ID,若无法发送则返回0")
    @SystemMessage("""
            您需要向未通过第一轮评审的求职候选人发送一封友好的邮件。
            同时,您需要将申请状态更新为“已拒绝”。
            您应返回已发送邮件的ID。
            """)
    @UserMessage("""
            被拒候选人:{{candidateContact}}
            
            申请职位:{{jobDescription}}
            """)
    int send(@V("candidateContact") String candidateContact, @V("jobDescription") String jobDescription);
}

邮件分支需要候选人的联系信息,示例用 candidate_contact.txt 作为输入:

候选人联系卡片:
姓名:John Doe
地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)
邮箱:john.doe.dev@protonmail.com
电话:+32 495 67 89 23

1.2 InterviewOrganizer (组织面试Agent)

public interface InterviewOrganizer {

    @Agent("为申请人安排现场面试")
    @SystemMessage("""
            您通过向所有相关员工发送日历邀请来安排现场会议,
            时间定在从当前日期起一周后的上午,时长为3小时。
            这是相关的职位空缺:{{jobDescription}}
            您还需通过祝贺邮件邀请候选人,告知面试详情,
            以及他/她来现场前需要注意的事项。
            最后,您需要将申请状态更新为“已邀请现场面试”。
            """)
    @UserMessage("""
            为此候选人安排现场面试会议(需遵守外部访客政策):{{candidateContact}}
            """)
    String organize(@V("candidateContact") String candidateContact, @V("jobDescription") String jobDescription);
}

二、(可选)定义工具

两个分支都需要业务工具配合:

拒绝流程:发送拒绝邮件,更新简历申请状态。

安排面试流程:通过工具获取当前日期、查询面试官等员工信息、创建面试日程、发送通知,最后更新申请状态。

下面定义相关工具。原文使用 mock 实现演示调用过程,不会据此确认真实邮件发送、日程创建或状态持久化。

2.1 OrganizingTools

public class OrganizingTools {

    @Tool
    public Date getCurrentDate(){
        return new Date();
    }

    @Tool("根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名")
    public List<String> getInvolvedEmployeesForInterview(@P("职位描述ID") String jobDescriptionId){
        // 演示用虚拟实现
        return new ArrayList<>(List.of(
                "Anna Bolena: hiring.manager@company.com",
                "Chris Durue: near.colleague@company.com",
                "Esther Finnigan: vp@company.com"));
    }

    @Tool("根据电子邮件地址为员工创建日程条目")
    public void createCalendarEntry(@P("员工电子邮件地址列表") List<String> emailAddress, @P("会议主题") String topic, @P("开始日期和时间,格式为yyyy-mm-dd hh:mm") String start, @P("结束日期和时间,格式为yyyy-mm-dd hh:mm") String end){
        // 演示用虚拟实现
        System.out.println("*** 已创建日程条目 ***");
        System.out.println("主题:" + topic);
        System.out.println("开始时间:" + start);
        System.out.println("结束时间:" + end);
    }

    @Tool
    public int sendEmail(@P("收件人电子邮件地址列表") List<String> to, @P("抄送电子邮件地址列表") List<String> cc, @P("邮件主题") String subject, @P("正文") String body){
        // 演示用虚拟实现
        System.out.println("*** 已发送邮件 ***");
        System.out.println("收件人:" + to);
        System.out.println("抄送:" + cc);
        System.out.println("主题:" + subject);
        System.out.println("正文:" + body);
        return 1234; // 虚拟邮件ID
    }

    @Tool
    public void updateApplicationStatus(@P("职位描述ID") String jobDescriptionId, @P("候选人(名,姓)") String candidateName, @P("新的申请状态") String newStatus){
        // 演示用虚拟实现
        System.out.println("*** 已更新申请状态 ***");
        System.out.println("职位描述ID:" + jobDescriptionId);
        System.out.println("候选人姓名:" + candidateName);
        System.out.println("新状态:" + newStatus);
    }
}

2.2 RAG 工具

通常每个公司都有一些自己的访客制度,比如:车应该停在哪,衣冠必须端正,来面试时必须前台登记等等。这些LLM显然不知道,需要借助RAG工具

@Component("ragProvider")
public class RagProvider {

    @Autowired
    @Qualifier("ollamaEmbeddingModel")
    OllamaEmbeddingModel ollamaEmbeddingModel;

    public ContentRetriever loadHouseRulesRetriever() {
        Document doc = loadDocument(toPath("documents/house_rules.txt"));
        InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();

        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .documentSplitter(DocumentSplitters.recursive(200, 10))
                .embeddingModel(ollamaEmbeddingModel)
                .embeddingStore(store)
                .build();

        ingestor.ingest(List.of(doc));

        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(store)
                .embeddingModel(ollamaEmbeddingModel)
                .maxResults(2)
                .minScore(0.8)
                .build();
    }

    public static Path toPath(String relativePath) {
        try {
            URL fileUrl = Utils.class.getClassLoader().getResource(relativePath);
            return Paths.get(fileUrl.toURI());
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }
}

house_rules.txt内容如下:

1.仅允许在停车场后方指定户外区域吸烟。
2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。
3.员工须至少提前24小时通过内部日历预订会议室。
4.自行车必须停放在主楼外侧的专用自行车停放区。
5.所有外部访客须凭有效身份证件在前台登记方可进入。
6.外部访客均须佩戴临时访客证,并确保证件清晰可见。

三、条件工作流示例

/**
 * 此示例演示了条件式智能体工作流。
 * 基于评分和候选人资料,我们将执行以下操作之一:
 * - 调用一个智能体,为该候选人的现场面试做好一切准备
 * - 调用一个智能体,发送一封友好的邮件,告知我们不会推进该候选人的申请
 * by 菩提树下的杨过(yjmyzz.cnblogs.com)
 */
@SpringBootApplication
public class _5a_Conditional_Workflow_Example {

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.class, args);
        ChatModel model = context.getBean("ollamaChatModel", ChatModel.class);
        RagProvider ragProvider = context.getBean("ragProvider", RagProvider.class);

        // 2. 在此包中定义两个子智能体:
        //      - EmailAssistant.java
        //      - InterviewOrganizer.java

        // 3. 使用AgenticServices创建所有智能体
        EmailAssistant emailAssistant = AgenticServices.agentBuilder(EmailAssistant.class)
                .chatModel(model)
                .tools(new OrganizingTools()) // 该智能体可以使用那里定义的所有工具
                .build();
        InterviewOrganizer interviewOrganizer = AgenticServices.agentBuilder(InterviewOrganizer.class)
                .chatModel(model)
                .tools(new OrganizingTools())
                .contentRetriever(ragProvider.loadHouseRulesRetriever()) // 这是如何为智能体添加RAG的方式
                .build();

        // 4. 构建条件式工作流
        UntypedAgent candidateResponder = AgenticServices // 使用UntypedAgent,除非您定义了合成的智能体,请参见_2_Sequential_Agent_Example
                .conditionalBuilder()
                .subAgents(agenticScope -> ((CvReview) agenticScope.readState("cvReview")).score >= 0.8, interviewOrganizer)
                .subAgents(agenticScope -> ((CvReview) agenticScope.readState("cvReview")).score < 0.8, emailAssistant)
                .build();
        // 重要提示:当定义了多个条件时,它们会按顺序执行。
        // 如果您想在这里并行执行,请使用异步智能体,如_5b_Conditional_Workflow_Example_Async中所示

        // 5. 从resources/documents/中的文本文件加载参数
        String candidateCv = StringLoader.loadFromResource("/documents/tailored_cv.txt");
        String candidateContact = StringLoader.loadFromResource("/documents/candidate_contact.txt");
        String jobDescription = StringLoader.loadFromResource("/documents/job_description_backend.txt");
        CvReview cvReviewFail = new CvReview(0.6, "简历不错,但缺少一些与后端职位相关的技术细节。");
        CvReview cvReviewPass = new CvReview(0.9, "简历非常出色,符合后端职位的所有要求。");

        // 5. 因为我们使用了无类型智能体,所以需要传递所有输入参数的映射
        Map<String, Object> arguments = Map.of(
                "candidateCv", candidateCv,
                "candidateContact", candidateContact,
                "jobDescription", jobDescription,
                "cvReview", cvReviewPass // 更改为cvReviewFail以查看另一个分支
        );

        // 5. 调用条件式智能体,根据评审结果对候选人做出回应
        candidateResponder.invoke(arguments);
        
    }
}

代码第 52 行传入评分为 0.9 的 cvReview,按示例条件进入安排面试的分支。换成 cvReviewFail 可观察拒绝分支;本站没有重新运行这两个分支。

时序图(简化版,原文标注为 AI 生成)

条件工作流:面试与拒绝分支:简化时序图

原文配图:条件工作流:面试与拒绝分支:简化时序图。

时序图(详细版,原文标注为 AI 生成)

条件工作流:面试与拒绝分支:详细时序图

原文配图:条件工作流:面试与拒绝分支:详细时序图。

运行结果:

展开原文日志或请求/响应(未复测)
2026-01-17T16:53:37.524+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/embed
- headers: [Content-Type: application/json]
- body: {
  "model" : "nomic-embed-text:latest",
  "input" : [ "1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。" ]
}

2026-01-17T16:53:42.826+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:53:42 GMT], [transfer-encoding: chunked]
- body: {"model":"nomic-embed-text:latest","embeddings":[[0.028599557,0.057497256,...-0.054139506]],"total_duration":5262584300,"load_duration":5247416900,"prompt_eval_count":161}

2026-01-17T16:53:42.871+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/embed
- headers: [Content-Type: application/json]
- body: {
  "model" : "nomic-embed-text:latest",
  "input" : [ "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n" ]
}

2026-01-17T16:53:42.895+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:53:42 GMT], [transfer-encoding: chunked]
- body: {"model":"nomic-embed-text:latest","embeddings":[[0.018723998,0.067336,...-0.042845238]],"total_duration":22005300,"load_duration":12774100,"prompt_eval_count":88}

2026-01-17T16:53:42.939+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
       ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
      }
    }
  } ]
}

2026-01-17T16:53:45.099+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:53:45 GMT], [transfer-encoding: chunked]
- body: {"model":"deepseek-v3.1:671b-cloud","remote_model":"deepseek-v3.1:671b","remote_host":"https://ollama.com:443","created_at":"2026-01-17T08:53:44.795645806Z","message":{"role":"assistant","content":"我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。","tool_calls":[{"id":"call_che9srkc","function":{"index":0,"name":"getCurrentDate","arguments":{}}}]},"done":true,"done_reason":"stop","total_duration":766029891,"prompt_eval_count":991,"eval_count":37}


2026-01-17T16:53:45.113+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  }, {
    "role" : "assistant",
    "content" : "我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。",
    "tool_calls" : [ {
      "function" : {
        "name" : "getCurrentDate",
        "arguments" : { }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1768640025109"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
       ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
        },
        "required" : [ "to", "cc", "subject", "body" ]
      }
    }
  } ]
}

2026-01-17T16:53:46.607+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:53:46 GMT], [transfer-encoding: chunked]
- body: {"model":"deepseek-v3.1:671b-cloud","remote_model":"deepseek-v3.1:671b","remote_host":"https://ollama.com:443","created_at":"2026-01-17T08:53:45.888540222Z","message":{"role":"assistant","content":"","tool_calls":[{"id":"call_pb0rggf6","function":{"index":0,"name":"getInvolvedEmployeesForInterview","arguments":{"jobDescriptionId":"123A"}}}]},"done":true,"done_reason":"stop","total_duration":645456006,"prompt_eval_count":1035,"eval_count":21}


2026-01-17T16:53:46.609+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  }, {
    "role" : "assistant",
    "content" : "我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。",
    "tool_calls" : [ {
      "function" : {
        "name" : "getCurrentDate",
        "arguments" : { }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1768640025109"
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "function" : {
        "name" : "getInvolvedEmployeesForInterview",
        "arguments" : {
          "jobDescriptionId" : "123A"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "[ \"Anna Bolena: hiring.manager@company.com\", \"Chris Durue: near.colleague@company.com\", \"Esther Finnigan: vp@company.com\" ]"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
      }
    }
  } ]
}

2026-01-17T16:53:50.266+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:53:50 GMT], [transfer-encoding: chunked]
- body: {"model":"deepseek-v3.1:671b-cloud","remote_model":"deepseek-v3.1:671b","remote_host":"https://ollama.com:443","created_at":"2026-01-17T08:53:48.769553461Z","message":{"role":"assistant","content":"现在我已经获取了需要参加面试的员工信息。让我创建面试会议日程:","tool_calls":[{"id":"call_qzbeevl7","function":{"index":0,"name":"createCalendarEntry","arguments":{"emailAddress":["hiring.manager@company.com","near.colleague@company.com","vp@company.com"],"end":"2025-11–21 12:00","start":"2025-11–21 09:00","topic":"后端工程师职位现场面试 - John Doe"}}}]},"done":true,"done_reason":"stop","total_duration":2032866290,"prompt_eval_count":1095,"eval_count":88}


*** 已创建日程条目 ***
主题:后端工程师职位现场面试 - John Doe
开始时间:2025-11–21 09:00
结束时间:2025-11–21 12:00
2026-01-17T16:53:50.272+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  }, {
    "role" : "assistant",
    "content" : "我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。",
    "tool_calls" : [ {
      "function" : {
        "name" : "getCurrentDate",
        "arguments" : { }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1768640025109"
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "function" : {
        "name" : "getInvolvedEmployeesForInterview",
        "arguments" : {
          "jobDescriptionId" : "123A"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "[ \"Anna Bolena: hiring.manager@company.com\", \"Chris Durue: near.colleague@company.com\", \"Esther Finnigan: vp@company.com\" ]"
  }, {
    "role" : "assistant",
    "content" : "现在我已经获取了需要参加面试的员工信息。让我创建面试会议日程:",
    "tool_calls" : [ {
      "function" : {
        "name" : "createCalendarEntry",
        "arguments" : {
          "emailAddress" : [ "hiring.manager@company.com", "near.colleague@company.com", "vp@company.com" ],
          "start" : "2025-11–21 09:00",
          "topic" : "后端工程师职位现场面试 - John Doe",
          "end" : "2025-11–21 12:00"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "Success"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
       ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
      }
    }
  } ]
}

2026-01-17T16:54:10.480+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:54:10 GMT], [transfer-encoding: chunked]
- body: {"model":"deepseek-v3.1:671b-cloud","remote_model":"deepseek-v3.1:671b","remote_host":"https://ollama.com:443","created_at":"2026-01-17T08:54:09.753507501Z","message":{"role":"assistant","content":"现在我将发送祝贺邮件给候选人John Doe,并告知面试详情:","tool_calls":[{"id":"call_eudsd6uf","function":{"index":0,"name":"sendEmail","arguments":{"body":"尊敬的John Doe先生/女士:\n\n恭喜您!您已成功通过我们后端工程师职位的初步筛选。我们诚挚邀请您参加现场面试。\n\n面试详情:\n- 日期:从当前日期起一周后(2025年11月21日)\n- 时间:上午9:00 - 12:00(时长3小时)\n- 地点:Rue des Carmes 12, 2000 Antwerp, Belgium\n\n现场面试注意事项:\n\n重要访客政策提醒:\n1. 所有外部访客须凭有效身份证件在前台登记方可进入\n2. 您将被授予临时访客证,请确保佩戴在显眼位置\n3. 仅允许在停车场后方指定户外区域吸烟\n4. 办公区安静时段为下午2点至4点,请避免在开放空间大声交谈\n5. 如有自行车,请停放在主楼外侧的专用自行车停放区\n\n我们期待与您见面,讨论您在金融科技后端开发方面的经验与潜力。\n\n祝好,\n招聘团队","cc":["hiring.manager@company.com"],"subject":"祝贺!后端工程师职位现场面试邀请","to":["john.doe.dev@protonmail.com"]}}}]},"done":true,"done_reason":"stop","total_duration":19351550481,"prompt_eval_count":1186,"eval_count":301}


*** 已发送邮件 ***
收件人:[john.doe.dev@protonmail.com]
抄送:[hiring.manager@company.com]
主题:祝贺!后端工程师职位现场面试邀请
正文:尊敬的John Doe先生/女士:

恭喜您!您已成功通过我们后端工程师职位的初步筛选。我们诚挚邀请您参加现场面试。

面试详情:
- 日期:从当前日期起一周后(2025年11月21日)
- 时间:上午9:00 - 12:00(时长3小时)
- 地点:Rue des Carmes 12, 2000 Antwerp, Belgium

现场面试注意事项:

重要访客政策提醒:
1. 所有外部访客须凭有效身份证件在前台登记方可进入
2. 您将被授予临时访客证,请确保佩戴在显眼位置
3. 仅允许在停车场后方指定户外区域吸烟
4. 办公区安静时段为下午2点至4点,请避免在开放空间大声交谈
5. 如有自行车,请停放在主楼外侧的专用自行车停放区

我们期待与您见面,讨论您在金融科技后端开发方面的经验与潜力。

祝好,
招聘团队
2026-01-17T16:54:10.484+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  }, {
    "role" : "assistant",
    "content" : "我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。",
    "tool_calls" : [ {
      "function" : {
        "name" : "getCurrentDate",
        "arguments" : { }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1768640025109"
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "function" : {
        "name" : "getInvolvedEmployeesForInterview",
        "arguments" : {
          "jobDescriptionId" : "123A"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "[ \"Anna Bolena: hiring.manager@company.com\", \"Chris Durue: near.colleague@company.com\", \"Esther Finnigan: vp@company.com\" ]"
  }, {
    "role" : "assistant",
    "content" : "现在我已经获取了需要参加面试的员工信息。让我创建面试会议日程:",
    "tool_calls" : [ {
      "function" : {
        "name" : "createCalendarEntry",
        "arguments" : {
          "emailAddress" : [ "hiring.manager@company.com", "near.colleague@company.com", "vp@company.com" ],
          "start" : "2025-11–21 09:00",
          "topic" : "后端工程师职位现场面试 - John Doe",
          "end" : "2025-11–21 12:00"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "Success"
  }, {
    "role" : "assistant",
    "content" : "现在我将发送祝贺邮件给候选人John Doe,并告知面试详情:",
    "tool_calls" : [ {
      "function" : {
        "name" : "sendEmail",
        "arguments" : {
          "cc" : [ "hiring.manager@company.com" ],
          "subject" : "祝贺!后端工程师职位现场面试邀请",
          "to" : [ "john.doe.dev@protonmail.com" ],
          "body" : "尊敬的John Doe先生/女士:\n\n恭喜您!您已成功通过我们后端工程师职位的初步筛选。我们诚挚邀请您参加现场面试。\n\n面试详情:\n- 日期:从当前日期起一周后(2025年11月21日)\n- 时间:上午9:00 - 12:00(时长3小时)\n- 地点:Rue des Carmes 12, 2000 Antwerp, Belgium\n\n现场面试注意事项:\n\n重要访客政策提醒:\n1. 所有外部访客须凭有效身份证件在前台登记方可进入\n2. 您将被授予临时访客证,请确保佩戴在显眼位置\n3. 仅允许在停车场后方指定户外区域吸烟\n4. 办公区安静时段为下午2点至4点,请避免在开放空间大声交谈\n5. 如有自行车,请停放在主楼外侧的专用自行车停放区\n\n我们期待与您见面,讨论您在金融科技后端开发方面的经验与潜力。\n\n祝好,\n招聘团队"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1234"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
       ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
      }
    }
  } ]
}

2026-01-17T16:54:12.455+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP response:
- status code: 200
- headers: [content-type: application/json; charset=utf-8], [date: Sat, 17 Jan 2026 08:54:12 GMT], [transfer-encoding: chunked]
- body: {"model":"deepseek-v3.1:671b-cloud","remote_model":"deepseek-v3.1:671b","remote_host":"https://ollama.com:443","created_at":"2026-01-17T08:54:11.733376347Z","message":{"role":"assistant","content":"最后,我将更新申请状态:","tool_calls":[{"id":"call_rmbx9gyb","function":{"index":0,"name":"updateApplicationStatus","arguments":{"candidateName":"John Doe","jobDescriptionId":"123A","newStatus":"已邀请现场面试"}}}]},"done":true,"done_reason":"stop","total_duration":1119737172,"prompt_eval_count":1491,"eval_count":39}


*** 已更新申请状态 ***
职位描述ID:123A
候选人姓名:John Doe
新状态:已邀请现场面试
2026-01-17T16:54:12.458+08:00  INFO 15192 --- [langchain4j-study] [           main] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "您通过向所有相关员工发送日历邀请来安排现场会议,\n时间定在从当前日期起一周后的上午,时长为3小时。\n这是相关的职位空缺:职位描述 ID: 123A\r\n后端工程师(金融科技,安特卫普)\r\n------------------------------------------------------------\r\n我们正在招聘一位**后端工程师**,协助我们构建并完善支付与对账服务。您将主要使用 **Java (Spring Boot)**,并与产品及运营团队紧密合作,确保金融交易系统的可靠性和可扩展性。\r\n\r\n**职责:**\r\n*   设计、实现并维护能够处理大规模支付与对账业务的后端服务。\r\n*   在云原生环境中使用 **PostgreSQL**、**Kafka**、**Docker** 和 **Kubernetes**。\r\n*   专注于构建可靠、幂等的系统,并具备强大的可观测性。\r\n*   支持将遗留服务迁移至现代的 REST API。\r\n\r\n**要求:**\r\n*   具备 Java 和 Spring Boot 后端开发的职业经验。\r\n*   深入理解关系型数据库和事件驱动架构。\r\n*   熟悉容器化技术和部署流程。\r\n*   有金融或定价系统相关经验者优先。\r\n*   要求英语流利,会荷兰语者更佳。\n您还需通过祝贺邮件邀请候选人,告知面试详情,\n以及他/她来现场前需要注意的事项。\n最后,您需要将申请状态更新为“已邀请现场面试”。\n"
  }, {
    "role" : "user",
    "content" : "为此候选人安排现场面试会议(需遵守外部访客政策):候选人联系卡片:\r\n姓名:John Doe\r\n地址:Rue des Carmes 12, 2000 Antwerp, Belgium(比利时安特卫普)\r\n邮箱:john.doe.dev@protonmail.com\r\n电话:+32 495 67 89 23\n\n\nAnswer using the following information:\n1.仅允许在停车场后方指定户外区域吸烟。\r\n2.办公区安静时段为下午2点至4点,请避免在开放空间大声交谈。\r\n3.员工须至少提前24小时通过内部日历预订会议室。\r\n4.自行车必须停放在主楼外侧的专用自行车停放区。\r\n5.所有外部访客须凭有效身份证件在前台登记方可进入。\r\n6.外部访客均须佩戴临时访客证,并确保证件清晰可见。"
  }, {
    "role" : "assistant",
    "content" : "我将为您安排John Doe的现场面试。首先让我获取当前日期,然后找到需要参加面试的相关员工,并安排会议。",
    "tool_calls" : [ {
      "function" : {
        "name" : "getCurrentDate",
        "arguments" : { }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1768640025109"
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "function" : {
        "name" : "getInvolvedEmployeesForInterview",
        "arguments" : {
          "jobDescriptionId" : "123A"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "[ \"Anna Bolena: hiring.manager@company.com\", \"Chris Durue: near.colleague@company.com\", \"Esther Finnigan: vp@company.com\" ]"
  }, {
    "role" : "assistant",
    "content" : "现在我已经获取了需要参加面试的员工信息。让我创建面试会议日程:",
    "tool_calls" : [ {
      "function" : {
        "name" : "createCalendarEntry",
        "arguments" : {
          "emailAddress" : [ "hiring.manager@company.com", "near.colleague@company.com", "vp@company.com" ],
          "start" : "2025-11–21 09:00",
          "topic" : "后端工程师职位现场面试 - John Doe",
          "end" : "2025-11–21 12:00"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "Success"
  }, {
    "role" : "assistant",
    "content" : "现在我将发送祝贺邮件给候选人John Doe,并告知面试详情:",
    "tool_calls" : [ {
      "function" : {
        "name" : "sendEmail",
        "arguments" : {
          "cc" : [ "hiring.manager@company.com" ],
          "subject" : "祝贺!后端工程师职位现场面试邀请",
          "to" : [ "john.doe.dev@protonmail.com" ],
          "body" : "尊敬的John Doe先生/女士:\n\n恭喜您!您已成功通过我们后端工程师职位的初步筛选。我们诚挚邀请您参加现场面试。\n\n面试详情:\n- 日期:从当前日期起一周后(2025年11月21日)\n- 时间:上午9:00 - 12:00(时长3小时)\n- 地点:Rue des Carmes 12, 2000 Antwerp, Belgium\n\n现场面试注意事项:\n\n重要访客政策提醒:\n1. 所有外部访客须凭有效身份证件在前台登记方可进入\n2. 您将被授予临时访客证,请确保佩戴在显眼位置\n3. 仅允许在停车场后方指定户外区域吸烟\n4. 办公区安静时段为下午2点至4点,请避免在开放空间大声交谈\n5. 如有自行车,请停放在主楼外侧的专用自行车停放区\n\n我们期待与您见面,讨论您在金融科技后端开发方面的经验与潜力。\n\n祝好,\n招聘团队"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "1234"
  }, {
    "role" : "assistant",
    "content" : "最后,我将更新申请状态:",
    "tool_calls" : [ {
      "function" : {
        "name" : "updateApplicationStatus",
        "arguments" : {
          "candidateName" : "John Doe",
          "newStatus" : "已邀请现场面试",
          "jobDescriptionId" : "123A"
        }
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "Success"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getInvolvedEmployeesForInterview",
      "description" : "根据给定的职位描述ID,查找需要参加现场面试的人员的电子邮件地址和姓名",
      "parameters" : {
       ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "createCalendarEntry",
      "description" : "根据电子邮件地址为员工创建日程条目",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getCurrentDate"
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "updateApplicationStatus",
      "parameters" : {
        ...
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sendEmail",
      "parameters" : {
        ...
      }
    }
  } ]
}

下面按原文日志的行号阅读调用过程。日程、邮件和状态更新均来自示例中的 mock 工具:

  • 3-8行,是RAG初始化时,将house_rules.txt(访客制度)向量化

RAG 初始化时对访客制度进行向量化

原文配图:RAG 初始化时对访客制度进行向量化。

  • 16-21行,这次RAG的向量化,可能有朋友会产生疑惑,根源在于:

InterviewOrganizer 绑定 RAG 工具的位置

原文配图:InterviewOrganizer 绑定 RAG 工具的位置。

这里将InterviewOrganizer实例附加了RAG工具后,RAG会将对应Agent中的用户Prompt自动向量化,即:

Agent 用户提示词的向量化过程

原文配图:Agent 用户提示词的向量化过程。

  • 30-86是首次与LLM交互,注意tools节点,这里把所有可用的工具(包括工具的入参及要求)也一并给了LLM,以便让LLM决定需要调用哪些工具。
  • 91行,LLM的首次回答中,指出需要调用工具:getCurrentDate , 后面就不一一解释,大家按顺序看下去,应该不难理解
  • 263行,表示已创建了面试的日程
  • 374行,表示已给相关干系人发送了面试邮件通知
  • 524行,表示更新了简历的申请状态